From f50f8daf06db3f87e2641e5e89c941af1572266d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Wed, 22 Jun 2022 12:19:15 +0800 Subject: [PATCH 01/12] add login to index.ts --- packages/cli/src/actions/appAction.ts | 6 +- packages/cli/src/actions/userAction.ts | 2 +- packages/cli/src/api/login.ts | 7 -- packages/cli/src/api/request.ts | 3 +- packages/cli/src/api/user.ts | 2 - packages/cli/src/apps.js | 118 ------------------------- packages/cli/src/index.ts | 25 ++++++ packages/cli/src/init.js | 74 ---------------- packages/cli/src/login.js | 49 ---------- packages/cli/src/login.ts | 4 +- packages/cli/src/utils/constants.ts | 2 - 11 files changed, 33 insertions(+), 259 deletions(-) delete mode 100644 packages/cli/src/api/login.ts delete mode 100644 packages/cli/src/apps.js delete mode 100644 packages/cli/src/init.js delete mode 100644 packages/cli/src/login.js diff --git a/packages/cli/src/actions/appAction.ts b/packages/cli/src/actions/appAction.ts index 4a160116b4..23eb8bfff7 100644 --- a/packages/cli/src/actions/appAction.ts +++ b/packages/cli/src/actions/appAction.ts @@ -9,17 +9,17 @@ export async function appListCommand() { //init table const table = new Table({ - head: ['APPId', 'name','status'], + head: ['appid', 'name','status'], }); - // create app + // user create app if(response.data.created){ response.data.created.forEach(app => { table.push([app.appid,app.name,app.status]) }); } - // join app + // user join app if(response.data.joined){ response.data.joined.forEach(app => { table.push([app.appid,app.name,app.status]) diff --git a/packages/cli/src/actions/userAction.ts b/packages/cli/src/actions/userAction.ts index da0f42629e..cf92d91d53 100644 --- a/packages/cli/src/actions/userAction.ts +++ b/packages/cli/src/actions/userAction.ts @@ -3,7 +3,7 @@ import * as fs from 'node:fs' import {AUTH_FILE} from '../utils/constants' import { checkCredentialsDir } from '../utils/util' -export async function loginCommand(remote:string,username:string,password:string) { +export async function handleLoginCommand(remote:string,username:string,password:string) { // check auth dir checkCredentialsDir() diff --git a/packages/cli/src/api/login.ts b/packages/cli/src/api/login.ts deleted file mode 100644 index 9d47387216..0000000000 --- a/packages/cli/src/api/login.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {postData }from "./request" - - - -export async function loginapi(obj:Object) { - return await postData('/sys-extension-api/func/password-login',obj) -} \ No newline at end of file diff --git a/packages/cli/src/api/request.ts b/packages/cli/src/api/request.ts index 8e7de8c224..0025c46397 100644 --- a/packages/cli/src/api/request.ts +++ b/packages/cli/src/api/request.ts @@ -18,7 +18,8 @@ request.interceptors.request.use( if (token) { config.headers.Authorization = `Bearer ${token}` } else { - config.headers.Authorization = 'Basic aGVhbHRoOmhlYWx0aA==' + console.error("please login first") + process.exit(1) } return config }, diff --git a/packages/cli/src/api/user.ts b/packages/cli/src/api/user.ts index 945c7a562c..9e0ecacc6e 100644 --- a/packages/cli/src/api/user.ts +++ b/packages/cli/src/api/user.ts @@ -12,8 +12,6 @@ export async function loginApi(server:string,obj:Object) { return false } - console.log(response.data) - return {access_token:response.data.access_token,expire_time:response.data.expire} diff --git a/packages/cli/src/apps.js b/packages/cli/src/apps.js deleted file mode 100644 index 28bd9cd442..0000000000 --- a/packages/cli/src/apps.js +++ /dev/null @@ -1,118 +0,0 @@ - -const { Command } = require('commander'); -const request = require('axios') -const path = require('path') -const fs = require('fs') -const dotenv = require('dotenv'); - -const Table = require('cli-table2') - -const homedir= require("os").homedir() -const CREDENTIALs_file = '.laf-credentials/auth.json'; -const envFile = path.resolve(homedir, CREDENTIALs_file) -const authData = JSON.parse(fs.readFileSync(envFile, 'utf8')); -const access_token = authData.access_token - -const program = new Command(); - -program -.command('list') -.option('--env ', `the file name to generate`, '.env') -.action(async ( options) => { - const url = `https://www.lafyun.com/sys-api/apps/my`; - const result = await request({ - method:"GET", - url, - headers:{ - authorization:`Bearer ${access_token}`, - } - }) - const response = result.data; - - if(response.data){ - const table = new Table({ - head: ['APPId', 'name','status'], - }); - - response.data.created.forEach(app => { - table.push([app.appid,app.name,app.status]) - }); - - response.data.joined.forEach(app => { - table.push([app.appid,app.name,app.status]) - }); - - console.log(table.toString()) - } - -}); - -program -.command('stop ') -.option('--env ', `the file name to generate`, '.env') -.action(async (appid,options) => { - - const url = `https://www.lafyun.com/sys-api/apps/${appid}/instance/stop`; - const result = await request({ - method:"POST", - url, - headers:{ - authorization:`Bearer ${access_token}`, - } - }) - const response = result.data; - - if(response.data.result){ - console.log('stop success') - }else{ - console.log('stop failed') - } -}); - - -program -.command('start ') -.option('--env ', `the file name to generate`, '.env') -.action(async (appid,options) => { - - const url = `https://www.lafyun.com/sys-api/apps/${appid}/instance/start`; - const result = await request({ - method:"POST", - url, - headers:{ - authorization:`Bearer ${access_token}`, - } - }) - const response = result.data; - - if(response.data.result){ - console.log('start success') - }else{ - console.log('start failed') - } -}); - - -program -.command('restart ') -.option('--env ', `the file name to generate`, '.env') -.action(async (appid,options) => { - - const url = `https://www.lafyun.com/sys-api/apps/${appid}/instance/restart`; - const result = await request({ - method:"POST", - url, - headers:{ - authorization:`Bearer ${access_token}`, - } - }) - const response = result.data; - - if(response.data.result){ - console.log('restart success') - }else{ - console.log('restart failed') - } -}); - -program.parse(process.argv); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 874789f78d..7fc5eebaa7 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -4,6 +4,7 @@ import { program } from 'commander' import * as dotenv from 'dotenv' import { resolve } from 'node:path' import { handleSyncCommand } from './sync' +import { handleLoginCommand } from './actions/userAction' import * as fs from 'node:fs' // laf-cli sync @@ -94,5 +95,29 @@ program console.log(' npm install -g laf-cli') }) + program + .command('login') + .option('-u, --username ', 'username') + .option('-p, --password ', 'password') + .option('-r, --remote', 'remote server', "https://console.lafyun.com/") + .action(async (options) => { + + // check params + const username = options.username + const password = options.password + if (!username) { + console.error('username is required') + process.exit(1) + } + if (!password) { + console.error('password is required') + process.exit(1) + } + + await handleLoginCommand(options.remote, username, password) + + + }) + program.parse(process.argv) diff --git a/packages/cli/src/init.js b/packages/cli/src/init.js deleted file mode 100644 index 87d5ad945f..0000000000 --- a/packages/cli/src/init.js +++ /dev/null @@ -1,74 +0,0 @@ - -const { Command } = require('commander'); -const request = require('axios') -const path = require('path') -const fs = require('fs') -const AdmZip = require('adm-zip'); -const program = new Command(); -const homedir= require("os").homedir() -const CREDENTIALs_file = '.laf-credentials/auth.json'; -const envFile = path.resolve(homedir, CREDENTIALs_file) -const authData = JSON.parse(fs.readFileSync(envFile, 'utf8')); -const access_token = authData.access_token - - -program - .command('init ') - .option('-s, --sync', 'sync app', false) - .action(async ( appid,options) => { - const url = `https://www.lafyun.com/sys-api/apps/${appid}/export`; - try{ - const result = await request({ - method:"GET", - url, - responseType: 'stream', - headers:{ - authorization:`Bearer ${access_token}`, - } - }) - - const appname = result.headers['content-disposition'].slice(22,-5); - - const apppath = path.resolve(process.cwd(), appname) - - try{ - fs.accessSync(apppath, fs.constants.R_OK|fs.constants.W_OK) - }catch(err){ - fs.mkdir(apppath, { recursive: true }, (err) => { - if (err) throw err; - }); - } - const lafFile = path.resolve(appname, 'laf.json') - - fs.writeFile(lafFile, JSON.stringify({appid:appid,root:"@laf"}),{ flag: 'w+' },(err) => { - if (err) throw err; - console.log('save,success'); - }) - - if(options.sync){ - - const appzip = appname+'.zip' - - const appzippath = path.resolve(process.cwd(), appzip) - - const writer = fs.createWriteStream(appzippath) - - result.data.pipe(writer) - - await new Promise((resolve, reject) => { - writer.on('finish', resolve) - writer.on('error', reject) - }) - - const file = new AdmZip(appzippath); - - file.extractAllTo(apppath) - } - } - catch(err){ - console.log(err.message) - } - - }); - -program.parse(process.argv); diff --git a/packages/cli/src/login.js b/packages/cli/src/login.js deleted file mode 100644 index cc5852d64d..0000000000 --- a/packages/cli/src/login.js +++ /dev/null @@ -1,49 +0,0 @@ - -const { Command } = require('commander'); -const request = require('axios') -const path = require('path') -const fs = require('fs') -const program = new Command(); -const homedir= require("os").homedir() -const CREDENTIALs_file = '.laf-credentials'; -program -.command('login') -.option('-u, --username ', 'username') -.option('-p, --password ', 'password') -.action(async ( options) => { - const envdir = path.resolve(homedir, CREDENTIALs_file) - fs.access(envdir, fs.constants.F_OK,(err) => { - console.log(`${envdir} ${err ? 'does not exist' : 'exists'}`); - if(err){ - fs.mkdir(envdir, { recursive: true }, (err) => { - if (err) throw err; - }); - } - }); - const username = options.username - const password = options.password - if(!username) { - console.error('username is required') - process.exit(1) - } - if(!password) { - console.error('password is required') - process.exit(1) - } - - const url = `https://www.lafyun.com/sys-extension-api/func/password-login`; - const result = await request.post(url,{username,password}) - const response = result.data; - if(response.code!=0){ - console.error('username or password is wrong') - process.exit(1) - } - const content = {access_token:response.data.access_token} - - const envFile = path.resolve(envdir, 'auth.json') - - fs.writeFileSync(envFile, JSON.stringify(content),{ flag: 'w+' }) - console.log(`Generated: ${envFile}`) -}); - -program.parse(process.argv); diff --git a/packages/cli/src/login.ts b/packages/cli/src/login.ts index fe43588bb3..e0407ecc1c 100644 --- a/packages/cli/src/login.ts +++ b/packages/cli/src/login.ts @@ -1,7 +1,7 @@ import { program } from 'commander' -import { loginCommand } from './actions/userAction' +import { handleLoginCommand } from './actions/userAction' program .command('login') @@ -22,7 +22,7 @@ program process.exit(1) } - await loginCommand(options.remote, username, password) + await handleLoginCommand(options.remote, username, password) }) diff --git a/packages/cli/src/utils/constants.ts b/packages/cli/src/utils/constants.ts index 1030888e81..6cae7a0713 100644 --- a/packages/cli/src/utils/constants.ts +++ b/packages/cli/src/utils/constants.ts @@ -3,10 +3,8 @@ import * as path from 'node:path' import {homedir} from 'node:os' const credentials_dir = ".laf-credentials" - const auth_file = "auth.json" -export const API_BASE_URL = "https://console.lafyun.com/" export const CREDENTIALS_DIR = path.resolve(homedir(),credentials_dir) export const AUTH_FILE = path.resolve(CREDENTIALS_DIR,auth_file) From c6600ad94e5f53e58bff168d90fe98366322fdda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Wed, 22 Jun 2022 12:31:25 +0800 Subject: [PATCH 02/12] add remarks --- packages/cli/src/api/request.ts | 4 ++-- packages/cli/src/api/user.ts | 5 +---- packages/cli/src/index.ts | 1 + packages/cli/src/utils/tokens.ts | 3 +++ packages/cli/src/utils/util.ts | 3 +++ 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/api/request.ts b/packages/cli/src/api/request.ts index 0025c46397..05e67477ef 100644 --- a/packages/cli/src/api/request.ts +++ b/packages/cli/src/api/request.ts @@ -6,14 +6,14 @@ import { getAccessToken } from '../utils/tokens' export const request = axios.create({ - // 联调 + // set baseURL baseURL: getRemoteServe() }) request.interceptors.request.use( async (config) => { - config.headers.VERSION = '' + const token = await getAccessToken() if (token) { config.headers.Authorization = `Bearer ${token}` diff --git a/packages/cli/src/api/user.ts b/packages/cli/src/api/user.ts index 9e0ecacc6e..464b25c72d 100644 --- a/packages/cli/src/api/user.ts +++ b/packages/cli/src/api/user.ts @@ -1,8 +1,7 @@ import axios from 'axios' - - export async function loginApi(server:string,obj:Object) { + // remote server login url const url = `${server}sys-extension-api/func/password-login`; const result = await axios.post(url,obj) @@ -13,6 +12,4 @@ export async function loginApi(server:string,obj:Object) { } return {access_token:response.data.access_token,expire_time:response.data.expire} - - } \ No newline at end of file diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 7fc5eebaa7..17232d54cb 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -114,6 +114,7 @@ program process.exit(1) } + // login await handleLoginCommand(options.remote, username, password) diff --git a/packages/cli/src/utils/tokens.ts b/packages/cli/src/utils/tokens.ts index b4c807f0c5..90fb46cba4 100644 --- a/packages/cli/src/utils/tokens.ts +++ b/packages/cli/src/utils/tokens.ts @@ -6,14 +6,17 @@ import { CREDENTIALS_DIR, AUTH_FILE } from '../utils/constants' export async function getAccessToken() { try { + // check dir fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK | fs.constants.W_OK) } catch (err) { console.error("please login first") process.exit(1) } + // read data const authData = JSON.parse(fs.readFileSync(AUTH_FILE, 'utf8')) + // check if expire const currentTime = Math.floor(Date.now() / 1000) if (currentTime > authData.expire_time) { console.log("access_token expire,please login") diff --git a/packages/cli/src/utils/util.ts b/packages/cli/src/utils/util.ts index 700c1513d1..81b4542ed1 100644 --- a/packages/cli/src/utils/util.ts +++ b/packages/cli/src/utils/util.ts @@ -5,8 +5,10 @@ import {AUTH_FILE} from '../utils/constants' export function checkCredentialsDir(){ try{ + // check dir fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK|fs.constants.W_OK) }catch(err){ + // mkdir fs.mkdirSync(CREDENTIALS_DIR, { recursive: true }) } @@ -15,6 +17,7 @@ export function checkCredentialsDir(){ export function getRemoteServe(){ try{ + // check dir fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK|fs.constants.W_OK) }catch(err){ console.error("please login first") From 77f910903cddde41e5ffd4fc5143658f1eabaf8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Wed, 22 Jun 2022 16:23:09 +0800 Subject: [PATCH 03/12] add remote server --- packages/cli/src/login.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/login.ts b/packages/cli/src/login.ts index e0407ecc1c..c760f8f681 100644 --- a/packages/cli/src/login.ts +++ b/packages/cli/src/login.ts @@ -7,7 +7,7 @@ program .command('login') .option('-u, --username ', 'username') .option('-p, --password ', 'password') - .option('-r, --remote', 'remote server', "https://console.lafyun.com/") + .option('-r, --remote ', 'remote server', "https://console.lafyun.com/") .action(async (options) => { // check params From 3ea2e988ddabef2aa244c46cc86f4d036b4a5645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Wed, 22 Jun 2022 16:25:14 +0800 Subject: [PATCH 04/12] add remote server --- packages/cli/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 17232d54cb..ca3615620d 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -99,7 +99,7 @@ program .command('login') .option('-u, --username ', 'username') .option('-p, --password ', 'password') - .option('-r, --remote', 'remote server', "https://console.lafyun.com/") + .option('-r, --remote ', 'remote server', "https://console.lafyun.com/") .action(async (options) => { // check params From 44a1f9d864b5eae928cc21a7289d2d4405d53941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Wed, 22 Jun 2022 16:31:20 +0800 Subject: [PATCH 05/12] add remote server --- packages/cli/src/api/user.ts | 2 +- packages/cli/src/index.ts | 2 +- packages/cli/src/login.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/api/user.ts b/packages/cli/src/api/user.ts index 464b25c72d..72a14d6acc 100644 --- a/packages/cli/src/api/user.ts +++ b/packages/cli/src/api/user.ts @@ -2,7 +2,7 @@ import axios from 'axios' export async function loginApi(server:string,obj:Object) { // remote server login url - const url = `${server}sys-extension-api/func/password-login`; + const url = `${server}/sys-extension-api/func/password-login`; const result = await axios.post(url,obj) const response = result.data diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index ca3615620d..a93c881312 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -99,7 +99,7 @@ program .command('login') .option('-u, --username ', 'username') .option('-p, --password ', 'password') - .option('-r, --remote ', 'remote server', "https://console.lafyun.com/") + .option('-r, --remote ', 'remote server', "https://console.lafyun.com") .action(async (options) => { // check params diff --git a/packages/cli/src/login.ts b/packages/cli/src/login.ts index c760f8f681..80c3b5b1de 100644 --- a/packages/cli/src/login.ts +++ b/packages/cli/src/login.ts @@ -7,7 +7,7 @@ program .command('login') .option('-u, --username ', 'username') .option('-p, --password ', 'password') - .option('-r, --remote ', 'remote server', "https://console.lafyun.com/") + .option('-r, --remote ', 'remote server', "https://console.lafyun.com") .action(async (options) => { // check params From a7bd6626d42abac73953e3fa022e75c2c77b66d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Thu, 23 Jun 2022 08:19:23 +0800 Subject: [PATCH 06/12] add response check --- packages/cli/src/api/request.ts | 15 +++++++++++++-- packages/cli/src/api/user.ts | 21 +++++++++++++++------ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/api/request.ts b/packages/cli/src/api/request.ts index 05e67477ef..be364e7037 100644 --- a/packages/cli/src/api/request.ts +++ b/packages/cli/src/api/request.ts @@ -10,7 +10,7 @@ export const request = axios.create({ baseURL: getRemoteServe() }) - +// http request request.interceptors.request.use( async (config) => { @@ -31,9 +31,20 @@ request.interceptors.request.use( }, ) +// http response +request.interceptors.response.use(function (response) { + // 对响应数据做点什么 + return response; + }, function (error) { + //return Promise.reject(err); + console.error(error.response.data) + process.exit(1) + }); + + /** - * 描述 axios post 请求 + * 描述 axios request 请求 * @param {Object} obj */ export function requestData(obj: object) { diff --git a/packages/cli/src/api/user.ts b/packages/cli/src/api/user.ts index 72a14d6acc..526658defd 100644 --- a/packages/cli/src/api/user.ts +++ b/packages/cli/src/api/user.ts @@ -3,13 +3,22 @@ import axios from 'axios' export async function loginApi(server:string,obj:Object) { // remote server login url const url = `${server}/sys-extension-api/func/password-login`; - const result = await axios.post(url,obj) - const response = result.data + try{ - if(response.code!=0){ - return false - } + const result = await axios.post(url,obj,{timeout:5000}) + + const response = result.data + + if(response.code!=0){ + return false + } - return {access_token:response.data.access_token,expire_time:response.data.expire} + return {access_token:response.data.access_token,expire_time:response.data.expire} + + }catch(err){ + + console.error(err.message) + process.exit(1) + } } \ No newline at end of file From 8cd51e7df632efdac93a167c68fdcb0f04625e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Thu, 23 Jun 2022 14:22:48 +0800 Subject: [PATCH 07/12] fix init function --- packages/cli/src/api/functions.ts | 21 +++ packages/cli/src/api/request.ts | 3 +- packages/cli/src/functions.js | 272 ---------------------------- packages/cli/src/functions.ts | 69 +++++++ packages/cli/src/init.ts | 35 ++-- packages/cli/src/utils/constants.ts | 6 +- packages/cli/src/utils/util.ts | 30 +++ 7 files changed, 142 insertions(+), 294 deletions(-) create mode 100644 packages/cli/src/api/functions.ts delete mode 100644 packages/cli/src/functions.js create mode 100644 packages/cli/src/functions.ts diff --git a/packages/cli/src/api/functions.ts b/packages/cli/src/api/functions.ts new file mode 100644 index 0000000000..815f503a8a --- /dev/null +++ b/packages/cli/src/api/functions.ts @@ -0,0 +1,21 @@ +import { requestData } from "./request" + + +export async function pullFunction(appid: string,functionName:string) { + + let url = '' + + if(functionName){ + url = `/sys-api/apps/${appid}/function?status=1&page=1&limit=100&keyword=${functionName}` + }else{ + url = `/sys-api/apps/${appid}/function?status=1&page=1&limit=100` + + } + const obj = { + method: "GET", + url + } + + const result = await requestData(obj) + return result.data +} \ No newline at end of file diff --git a/packages/cli/src/api/request.ts b/packages/cli/src/api/request.ts index be364e7037..e281127510 100644 --- a/packages/cli/src/api/request.ts +++ b/packages/cli/src/api/request.ts @@ -37,7 +37,8 @@ request.interceptors.response.use(function (response) { return response; }, function (error) { //return Promise.reject(err); - console.error(error.response.data) + console.error(error.response.status) + console.error(error.response.statusText) process.exit(1) }); diff --git a/packages/cli/src/functions.js b/packages/cli/src/functions.js deleted file mode 100644 index e7a781fd5c..0000000000 --- a/packages/cli/src/functions.js +++ /dev/null @@ -1,272 +0,0 @@ - -const { Command } = require('commander'); -const request = require('axios') -const path = require('path') -const fs = require('fs') - -const ts = require('typescript') - - -const jwt = require( 'jsonwebtoken') -const DEFAULT_SALT = 'Rewrite_Your_Own_Secret_Salt_abcdefg1234567' - -function getToken(payload) { - return jwt.sign(payload, DEFAULT_SALT) -} - - -function compileTs2js(source) { - const jscode = ts.transpile(source, { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2017, - removeComments: true, - }) - - return jscode - } - - -const homedir= require("os").homedir() -const CREDENTIALs_file = '.laf-credentials/auth.json'; -const envFile = path.resolve(homedir, CREDENTIALs_file) -const authData = JSON.parse(fs.readFileSync(envFile, 'utf8')); -const access_token = authData.access_token - -const appFile = path.resolve(process.cwd(), 'laf.json') -const appData = JSON.parse(fs.readFileSync(appFile, 'utf8')); - - -const program = new Command(); -program -.command('fn-pull') -.argument('[function-name]',"funct") -.option('-f, --force-overwrite', 'force to updated all files ignore if modified', false) -.option('--env ', `the file name to generate`, '../.env') -.action(async ( functionName,options) => { - - let url = ''; - - if(functionName){ - url = `https://www.lafyun.com/sys-api/apps/${appData.appid}/function?status=1&page=1&limit=100&keyword=${functionName}` - }else{ - url = `https://www.lafyun.com/sys-api/apps/${appData.appid}/function?status=1&page=1&limit=100` - - } - - const result = await request({ - method:"GET", - url, - headers:{ - authorization:`Bearer ${access_token}`, - } - }) - const response = result.data; - - console.log(response.data) - - const functionsDir = path.resolve(process.cwd(), 'functions') - - response.data.forEach(element => { - const funcName =element.name; - const funcFile = funcName+'/test.ts' - const funcPath = path.resolve(functionsDir, funcFile) - fs.writeFile(funcPath, element.code, err => { - if (err) { - console.error(err) - return - } - console.log('pull success') - }) - }); - - -}); - - - -program -.command('fn-invork') -.argument('[function-name]',"function-name") -.action(async ( functionName,options) => { - - const functionsDir = path.resolve(process.cwd(), 'functions') - - const functions = getAllFunctions(functionsDir) - - if(functionName){ - if(!functions.includes(functionName)){ - console.error('function not exist') - process.exit(1) - } - - } - - console.log(functionName) - - const funcFile= path.resolve(functionsDir, `${functionName}/index.ts`) - - - console.log(funcFile) - - const code = fs.readFileSync(funcFile, 'utf8') - - const exp = Math.floor(Date.now() / 1000) + 60 * 60 * 24 - - const appid = appData.appid; - - const token = getToken({ appid, type: 'debug', exp }) - - console.log(token) - - try{ - - const url = `https://${appData.appid}.lafyun.com/debug/${functionName}` - - const result = await request({ - method:"POST", - url, - headers:{ - authorization:`Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBpZCI6InZnMnA3NSIsInR5cGUiOiJkZWJ1ZyIsImV4cCI6MTY1NTcwMzUzMiwiaWF0IjoxNjU1NjE3MTMyfQ.aWc3SGiA4MAfv9-Uso9jb-83SP5DXeNRzYVzMvoByRk`, - }, - data:{ - func:{ - appid: appid, - code: code, - compiledCode: compileTs2js(code), - debugParams: "{\n \"code\": \"laf\"\n}", - }, - param:{code:"laf"} - } - }) - - console.log(result.data) - - } catch(e){ - - console.log(e.message) - - } - -}); - - - -program -.command('fn-push') -.argument('[function-name]',"function-name") -.action(async ( functionName,options) => { - - const functionsDir = path.resolve(process.cwd(), 'functions') - - const functions = getAllFunctions(functionsDir) - - if(functionName){ - if(!functions.includes(functionName)){ - console.error('function not exist') - process.exit(1) - } - - } - - console.log(functionName) - - const funcFile= path.resolve(functionsDir, `${functionName}/index.ts`) - - - console.log(funcFile) - - const code = fs.readFileSync(funcFile, 'utf8') - - - const metaFile = functionName+'/meta.json' - const metaFilePath = path.resolve(functionsDir, metaFile) - - const metaData = JSON.parse(fs.readFileSync(metaFilePath, 'utf8')); - - // 读取远程函数列表 判读函数是否存在 - - - const url = `https://www.lafyun.com/sys-api/apps/${appData.appid}/function/${metaData.id}/code` - - const result = await request({ - method:"POST", - url, - headers:{ - authorization:`Bearer ${access_token}`, - }, - data:{ - code:code.toString() - } - }) - - console.log(result.data) - -}); - - - - -program -.command('fn-pub') -.argument('[function-name]',"function-name") -.option('-a, --all', 'force to updated all files ignore if modified', false) -.action(async ( functionName,options) => { - - const functionsDir = path.resolve(process.cwd(), 'functions') - - const functions = getAllFunctions(functionsDir) - let publishFunctions = []; - if(functionName){ - if(!functions.includes(functionName)){ - console.error('function not exist') - process.exit(1) - } - - publishFunctions.push(functionName) - - }else{ - - publishFunctions = functions; - } - - publishFunctions.forEach(async(item) =>{ - - const metaFile = item+'/meta.json' - const metaFilePath = path.resolve(functionsDir, metaFile) - - const metaData = JSON.parse(fs.readFileSync(metaFilePath, 'utf8')); - - console.log(metaData) - - const url = `https://www.lafyun.com/sys-api/apps/${appData.appid}/function/${metaData.id}/publish` - - const result = await request({ - method:"POST", - url, - headers:{ - authorization:`Bearer ${access_token}`, - } - }) - console.log(result.data) - - }) - -}); - - - - -program.parse(process.argv); - -function getAllFunctions(dir){ - let arrFiles = [] - const files = fs.readdirSync(dir) - for (let i = 0; i < files.length; i++) { - const item = files[i] - const stat = fs.lstatSync(dir + '\\' + item) - if (stat.isDirectory() === true) { - arrFiles.push(item) - } - } - return arrFiles; -} \ No newline at end of file diff --git a/packages/cli/src/functions.ts b/packages/cli/src/functions.ts new file mode 100644 index 0000000000..2e39d61a00 --- /dev/null +++ b/packages/cli/src/functions.ts @@ -0,0 +1,69 @@ + +import { program } from 'commander' +import * as path from 'node:path' +import * as fs from 'node:fs' +import { pullFunction } from './api/functions' + +import { LAF_FILE , FUNCTIONS_DIR ,FUNCTIONS_FILE} from './utils/constants' +import { checkDir } from './utils/util' + +const appFile = path.resolve(process.cwd(), LAF_FILE) +const appData = JSON.parse(fs.readFileSync(appFile, 'utf8')) + +program +.command('fn-pull') +.argument('[function-name]',"functionname") +.option('-f, --force-overwrite', 'force to file ignore if modified', false) +.action(async ( functionName,options) => { + + // pull function + const response =await pullFunction(appData.appid,functionName) + + if(response.total){ + + // functions dir + const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) + + checkDir(functionsDir) + + response.data.forEach(element => { + + //fuction name + const funcName =element.name; + const funcNameDir = path.resolve(functionsDir, funcName) + + checkDir(funcNameDir) + + const funcFile= path.resolve(funcNameDir, FUNCTIONS_FILE) + try{ + // check if exist function file + fs.accessSync(funcFile) + const currentCode =fs.readFileSync(funcFile,'utf-8') + + if(currentCode){ + // forceOverwrite + if(options.forceOverwrite){ + fs.writeFileSync(funcFile, element.code) + } + }else{ + fs.writeFileSync(funcFile, element.code) + } + }catch(err){ + + fs.writeFileSync(funcFile, element.code) + + } + + console.log('pull success') + }) + + }else{ + + console.log('functions not find') + } + +}) + + +program.parse(process.argv); + diff --git a/packages/cli/src/init.ts b/packages/cli/src/init.ts index 4972cda235..dcf6b60b7c 100644 --- a/packages/cli/src/init.ts +++ b/packages/cli/src/init.ts @@ -5,7 +5,9 @@ import * as fs from 'node:fs' import * as AdmZip from 'adm-zip' import * as path from 'node:path' -import { LAF_FILE } from './config/config' +import { LAF_FILE } from './utils/constants' + +import {checkDir } from './utils/util' program .command('init ') @@ -16,31 +18,24 @@ program const result = await initApi(appid) - const appname = result.headers['content-disposition'].slice(22, -5) + const appName = result.headers['content-disposition'].slice(22, -5) - const apppath = path.resolve(process.cwd(), appname) + const appPath = path.resolve(process.cwd(), appName) - try { - fs.accessSync(apppath, fs.constants.R_OK | fs.constants.W_OK) - } catch (err) { - fs.mkdir(apppath, { recursive: true }, (err) => { - if (err) throw err - }) - } - const lafFile = path.resolve(appname, LAF_FILE) + checkDir(appPath) + const lafFile = path.resolve(appName, LAF_FILE) + + fs.writeFileSync(lafFile, JSON.stringify({ appid: appid, root: "@laf" })) - fs.writeFile(lafFile, JSON.stringify({ appid: appid, root: "@laf" }), { flag: 'w+' }, (err) => { - if (err) throw err - console.log('save,success') - }) + console.log('save success') if (options.sync) { - const appzip = appname + '.zip' + const appZip = appName + '.zip' - const appzippath = path.resolve(process.cwd(), appzip) + const appZipPath = path.resolve(process.cwd(), appZip) - const writer = fs.createWriteStream(appzippath) + const writer = fs.createWriteStream(appZipPath) result.data.pipe(writer) @@ -49,9 +44,9 @@ program writer.on('error', reject) }) - const file = new AdmZip(appzippath) + const file = new AdmZip(appZipPath) - file.extractAllTo(apppath) + file.extractAllTo(appPath) } } catch (err) { diff --git a/packages/cli/src/utils/constants.ts b/packages/cli/src/utils/constants.ts index 6cae7a0713..81efd8e742 100644 --- a/packages/cli/src/utils/constants.ts +++ b/packages/cli/src/utils/constants.ts @@ -8,4 +8,8 @@ const auth_file = "auth.json" export const CREDENTIALS_DIR = path.resolve(homedir(),credentials_dir) export const AUTH_FILE = path.resolve(CREDENTIALS_DIR,auth_file) -export const LAF_FILE = "laf.json" \ No newline at end of file +export const LAF_FILE = "laf.json" + +export const FUNCTIONS_DIR = "functions" + +export const FUNCTIONS_FILE = "index.ts" \ No newline at end of file diff --git a/packages/cli/src/utils/util.ts b/packages/cli/src/utils/util.ts index 81b4542ed1..65b1feb5d0 100644 --- a/packages/cli/src/utils/util.ts +++ b/packages/cli/src/utils/util.ts @@ -1,4 +1,5 @@ import * as fs from 'node:fs' +import * as path from 'node:path' import {CREDENTIALS_DIR} from './constants' import {AUTH_FILE} from '../utils/constants' @@ -14,6 +15,17 @@ export function checkCredentialsDir(){ } +export function checkDir(dir:string){ + try{ + // check dir + fs.accessSync(dir, fs.constants.R_OK|fs.constants.W_OK) + }catch(err){ + // mkdir + fs.mkdirSync(dir, { recursive: true }) + } + +} + export function getRemoteServe(){ try{ @@ -28,4 +40,22 @@ export function getRemoteServe(){ return authData.remote } + +export function getAppFunctions(dir:string){ + let arrFiles = [] + const files = fs.readdirSync(dir) + for (let i = 0; i < files.length; i++) { + + const item = files[i] + + const itemDir = path.resolve(dir,item) + + const stat = fs.lstatSync(itemDir) + if (stat.isDirectory() === true) { + arrFiles.push(item) + } + } + return arrFiles; +} + \ No newline at end of file From f5d82cbdbe08ff2c3341bf45a67564bc8a0fdcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Thu, 23 Jun 2022 14:24:39 +0800 Subject: [PATCH 08/12] fix init function --- packages/cli/src/apps.ts | 2 +- packages/cli/src/init.ts | 15 ++++++++------- packages/cli/src/utils/util.ts | 1 + 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/apps.ts b/packages/cli/src/apps.ts index 34f82e61cf..e3b4b9b3e0 100644 --- a/packages/cli/src/apps.ts +++ b/packages/cli/src/apps.ts @@ -6,7 +6,7 @@ import { appListCommand } from './actions/appAction' program .command('list') .action(async () => { - + await appListCommand() }) diff --git a/packages/cli/src/init.ts b/packages/cli/src/init.ts index dcf6b60b7c..e83766e018 100644 --- a/packages/cli/src/init.ts +++ b/packages/cli/src/init.ts @@ -7,7 +7,7 @@ import * as path from 'node:path' import { LAF_FILE } from './utils/constants' -import {checkDir } from './utils/util' +import { checkDir } from './utils/util' program .command('init ') @@ -16,8 +16,10 @@ program try { + // get app const result = await initApi(appid) + // get app name const appName = result.headers['content-disposition'].slice(22, -5) const appPath = path.resolve(process.cwd(), appName) @@ -25,27 +27,26 @@ program checkDir(appPath) const lafFile = path.resolve(appName, LAF_FILE) - fs.writeFileSync(lafFile, JSON.stringify({ appid: appid, root: "@laf" })) + // write data + fs.writeFileSync(lafFile, JSON.stringify({ appid: appid, root: "@laf" ,endPoint:""})) console.log('save success') + // sync app data if (options.sync) { + // add app data to zip const appZip = appName + '.zip' - const appZipPath = path.resolve(process.cwd(), appZip) - const writer = fs.createWriteStream(appZipPath) - result.data.pipe(writer) - await new Promise((resolve, reject) => { writer.on('finish', resolve) writer.on('error', reject) }) + // unzip const file = new AdmZip(appZipPath) - file.extractAllTo(appPath) } } diff --git a/packages/cli/src/utils/util.ts b/packages/cli/src/utils/util.ts index 65b1feb5d0..1fcce32a66 100644 --- a/packages/cli/src/utils/util.ts +++ b/packages/cli/src/utils/util.ts @@ -3,6 +3,7 @@ import * as path from 'node:path' import {CREDENTIALS_DIR} from './constants' import {AUTH_FILE} from '../utils/constants' +// check auth dir export function checkCredentialsDir(){ try{ From 79f14345733e15724b7cd5446011ecfed03f0e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Mon, 27 Jun 2022 08:58:07 +0800 Subject: [PATCH 09/12] fix command --- packages/cli/src/actions/appAction.ts | 7 +- packages/cli/src/actions/functionAction.ts | 146 +++++++++++++++++++++ packages/cli/src/actions/initActiont.ts | 51 +++++++ packages/cli/src/actions/userAction.ts | 8 ++ packages/cli/src/api/apps.ts | 39 ++++++ packages/cli/src/api/functions.ts | 124 +++++++++++++++++ packages/cli/src/api/request.ts | 53 ++++++-- packages/cli/src/api/{init.ts => sync.ts} | 8 +- packages/cli/src/api/user.ts | 9 +- packages/cli/src/apps.ts | 4 +- packages/cli/src/config/config.ts | 13 -- packages/cli/src/functions.ts | 96 ++++++++------ packages/cli/src/init.ts | 49 ++----- packages/cli/src/utils/tokens.ts | 25 +++- packages/cli/src/utils/util-lang.ts | 18 +++ packages/cli/src/utils/util.ts | 89 ++++++++++--- 16 files changed, 611 insertions(+), 128 deletions(-) create mode 100644 packages/cli/src/actions/functionAction.ts create mode 100644 packages/cli/src/actions/initActiont.ts rename packages/cli/src/api/{init.ts => sync.ts} (64%) delete mode 100644 packages/cli/src/config/config.ts create mode 100644 packages/cli/src/utils/util-lang.ts diff --git a/packages/cli/src/actions/appAction.ts b/packages/cli/src/actions/appAction.ts index 23eb8bfff7..f1121b807a 100644 --- a/packages/cli/src/actions/appAction.ts +++ b/packages/cli/src/actions/appAction.ts @@ -2,7 +2,12 @@ import * as Table from 'cli-table2' import {appList} from '../api/apps' -export async function appListCommand() { +/** + * apps list + * @returns + */ + +export async function handleAppListCommand() { // get list const response = await appList() diff --git a/packages/cli/src/actions/functionAction.ts b/packages/cli/src/actions/functionAction.ts new file mode 100644 index 0000000000..ca4065c0a1 --- /dev/null +++ b/packages/cli/src/actions/functionAction.ts @@ -0,0 +1,146 @@ + +import * as path from 'node:path' +import * as fs from 'node:fs' +import { compileTs2js } from '../utils/util-lang' + +import { FUNCTIONS_DIR ,FUNCTIONS_FILE} from '../utils/constants' +import { checkDir} from '../utils/util' + +import { debugFunction,getFunctionByName,pushFunction,createFunction} from '../api/functions' + + + +/** + * pull function + * @param {any} data + * @param {any} options + * @returns + */ + +export async function handlePullFunctionCommand(data:any,options:any) { + + // functions dir + const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) + + checkDir(functionsDir) + + data.forEach(element => { + + //fuction name + const funcName =element.name; + const funcNameDir = path.resolve(functionsDir, funcName) + + checkDir(funcNameDir) + + const funcFile= path.resolve(funcNameDir, FUNCTIONS_FILE) + try{ + // check if exist function file + fs.accessSync(funcFile) + const currentCode =fs.readFileSync(funcFile,'utf-8') + + if(currentCode){ + // forceOverwrite + if(options.forceOverwrite){ + fs.writeFileSync(funcFile, element.code) + } + }else{ + fs.writeFileSync(funcFile, element.code) + } + }catch(err){ + + fs.writeFileSync(funcFile, element.code) + + } + + console.log('pull success') + }) + + +} + +/** + * invoke function + * @param {string} appid + * @param {string} functionName + * @returns + */ + +export async function handleInvokeFunctionCommand(appid:string,functionName:string,param:object) { + + const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) + + // get local code + const functionNameDir = path.resolve(functionsDir, functionName) + const funcFile= path.resolve(functionNameDir, FUNCTIONS_FILE) + const code = fs.readFileSync(funcFile, 'utf8') + const obj = { + func:{ + appid: appid, + code: code, + name:functionName, + compiledCode: compileTs2js(code), + debugParams: JSON.stringify(param), + }, + param:param + } + + const res = await debugFunction(appid,'test',obj) + console.log(res) + +} + + +/** + * push fuction + * @param {string} appid + * @param {string} functionName + * @param {any} options + * @returns + */ + + +export async function handlePushFunctionCommand(appid:string,functionName:string,options:any) { + + const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) + + // get local code + const functionNameDir = path.resolve(functionsDir, functionName) + const funcFile= path.resolve(functionNameDir, FUNCTIONS_FILE) + const code = fs.readFileSync(funcFile, 'utf8') + + // get function + const record = await getFunctionByName(appid,functionName) + + //update function + if(record.data){ + if(record.data.code!==code){ + + if(options.forceOverwrite){ + const data = { + code:code, + debugParams:JSON.stringify({"code":"laf"}), + } + const res = await pushFunction(appid,functionName,data) + if(res.data){ + console.log("push success") + } + } + }else{ + console.log("push success1") + } + + }else{ + // create function + const data = { + code:code, + name:functionName, + label:"test", + } + + const res = await createFunction(appid,data) + if(res.data){ + console.log("push success") + } + } + +} \ No newline at end of file diff --git a/packages/cli/src/actions/initActiont.ts b/packages/cli/src/actions/initActiont.ts new file mode 100644 index 0000000000..001c67c5c5 --- /dev/null +++ b/packages/cli/src/actions/initActiont.ts @@ -0,0 +1,51 @@ +import * as fs from 'node:fs' +import * as path from 'node:path' +import { LAF_FILE } from '../utils/constants' +import { checkDir } from '../utils/util' + +import * as AdmZip from 'adm-zip' +import { pipeline } from 'node:stream/promises' + + +/** + * init app + * @param {string} funcName + * @param {string} appid + * @param {string} endPoint + * @returns + */ + +export async function handleInitAppCommand(appName:string,appid:string,endPoint:string) { + + const appPath = path.resolve(process.cwd(), appName) + checkDir(appPath) + const lafFile = path.resolve(appPath, LAF_FILE) + // write data + fs.writeFileSync(lafFile, JSON.stringify({ appid: appid, root: appPath ,endPoint})) + +} + + +/** + * sync app + * @param {string} funcName + * @param {any} data + * @returns + */ + +export async function handleSyncAppCommand(appName:string,data:any) { + + const appPath = path.resolve(process.cwd(), appName) + + const appZip = appName + '.zip' + const appZipPath = path.resolve(process.cwd(), appZip) + const writer = fs.createWriteStream(appZipPath) + await pipeline(data.data,writer); + + // unzip + const file = new AdmZip(appZipPath) + file.extractAllTo(appPath) + + fs.unlinkSync(appZipPath) + +} \ No newline at end of file diff --git a/packages/cli/src/actions/userAction.ts b/packages/cli/src/actions/userAction.ts index cf92d91d53..3955ec4691 100644 --- a/packages/cli/src/actions/userAction.ts +++ b/packages/cli/src/actions/userAction.ts @@ -3,6 +3,14 @@ import * as fs from 'node:fs' import {AUTH_FILE} from '../utils/constants' import { checkCredentialsDir } from '../utils/util' +/** + * login + * @param {string} remote + * @param {string} username + * @param {string} password + * @returns + */ + export async function handleLoginCommand(remote:string,username:string,password:string) { // check auth dir diff --git a/packages/cli/src/api/apps.ts b/packages/cli/src/api/apps.ts index 99ab215d61..f3c3ad7acb 100644 --- a/packages/cli/src/api/apps.ts +++ b/packages/cli/src/api/apps.ts @@ -1,5 +1,26 @@ import { requestData } from "./request" + +/** + * 根据 appid 获取应用 + * @param {string} appid + * @returns 返回应用数据 + */ + export async function getApplicationByAppid(appid:string) { + const res = await requestData({ + url: `/sys-api/apps/${appid}`, + method: 'get' + }) + + return res.data + } + + +/** + * 获取应用列表 + * @returns 返回应用数据 + */ + export async function appList() { const url = `/sys-api/apps/my` const obj = { @@ -10,6 +31,12 @@ export async function appList() { return result.data } +/** + * 根据 appid stop 应用 + * @param {string} appid + * @returns + */ + export async function appStop(appid: string) { const url = `/sys-api/apps/${appid}/instance/stop` const obj = { @@ -20,6 +47,12 @@ export async function appStop(appid: string) { return result.data } +/** + * 根据 appid start 应用 + * @param {string} appid + * @returns + */ + export async function appStart(appid: string) { const url = `/sys-api/apps/${appid}/instance/start` const obj = { @@ -31,6 +64,12 @@ export async function appStart(appid: string) { } +/** + * 根据 appid restart 应用 + * @param {string} appid + * @returns + */ + export async function appRestart(appid: string) { const url = `/sys-api/apps/${appid}/instance/start` const obj = { diff --git a/packages/cli/src/api/functions.ts b/packages/cli/src/api/functions.ts index 815f503a8a..8c78055b37 100644 --- a/packages/cli/src/api/functions.ts +++ b/packages/cli/src/api/functions.ts @@ -1,6 +1,16 @@ import { requestData } from "./request" +import { getDebugToken } from "../utils/tokens" +import { getAppData } from "../utils/util" +import axios from 'axios' +/** + * 根据 appid 同步函数 + * @param {string} appid + * @param {string} functionName + * @returns + */ + export async function pullFunction(appid: string,functionName:string) { let url = '' @@ -18,4 +28,118 @@ export async function pullFunction(appid: string,functionName:string) { const result = await requestData(obj) return result.data +} + +/** + * 调试函数 + * @param {string} appid + * @param {string} functionName + * @param {Object} obj + * @returns + */ + +export async function debugFunction(appid: string,functionName:string,obj:Object) { + + const appData = getAppData(); + + const url = `http://${appid}.${appData.endPoint}/debug/${functionName}` + + try{ + + const debug_token = await getDebugToken() + const headers ={"authorization":`Bearer ${debug_token}`} + + const result = await axios.post(url,obj,{headers:headers}) + + const response = result.data + + return response + + }catch(err){ + + console.error(err.message) + process.exit(1) + } + +} + +/** + * 创建函数 + * @param {string} appid + * @param {Object} data + * @returns + */ + +export async function createFunction(appid: string,data:Object) { + + + const url= `/sys-api/apps/${appid}/function/create` + + const obj = { + method: "POST", + url, + data + } + + const result = await requestData(obj) + return result.data +} + + +/** + * 获取函数 + * @param {string} appid + * @param {string} functionName + * @returns + */ + +export async function getFunctionByName(appid: string,functionName:string) { + const url= `/sys-api/apps/${appid}/function/getFunction/${functionName}` + const obj = { + method: "GET", + url, + } + const result = await requestData(obj) + return result.data +} + +/** + * 同步函数 + * @param {string} appid + * @param {string} functionName + * @param {Object} data + * @returns + */ + + +export async function pushFunction(appid: string,functionName:string,data:object) { + const url= `/sys-api/apps/${appid}/function/updateFunction/${functionName}` + + const obj = { + method: "POST", + url, + data + } + + const result = await requestData(obj) + return result.data +} + + +/** + * 发布函数 + * @param {string} appid + * @param {string} functionName + * @returns + */ + +export async function publishFunction(appid: string,functionName:string) { + const url= `/sys-api/apps/${appid}/function/publishFunction/${functionName}` + + const obj = { + method: "POST", + url + } + const result = await requestData(obj) + return result.data } \ No newline at end of file diff --git a/packages/cli/src/api/request.ts b/packages/cli/src/api/request.ts index e281127510..0ed49fb333 100644 --- a/packages/cli/src/api/request.ts +++ b/packages/cli/src/api/request.ts @@ -31,16 +31,49 @@ request.interceptors.request.use( }, ) -// http response -request.interceptors.response.use(function (response) { - // 对响应数据做点什么 - return response; - }, function (error) { - //return Promise.reject(err); - console.error(error.response.status) - console.error(error.response.statusText) - process.exit(1) - }); + + + request.interceptors.response.use( + /** + * If you want to get http information such as headers or status + * Please return response => response + */ + + /** + * Determine the request status by custom code + * Here is just an example + * You can also judge the status by HTTP Status Code + */ + response => { + return response + }, + error => { + const status = error.response.status + + if (status === 401) { + + console.error(error.response.data) + + process.exit(1) + + } + if (status === 403) { + + console.error(error.response.data) + + process.exit(1) + } + if (status === 422) { + + console.error(error.response.data) + + process.exit(1) + } + + // showError(error.message) + return Promise.reject(error) + } + ) diff --git a/packages/cli/src/api/init.ts b/packages/cli/src/api/sync.ts similarity index 64% rename from packages/cli/src/api/init.ts rename to packages/cli/src/api/sync.ts index 7779169ee4..25bade79f8 100644 --- a/packages/cli/src/api/init.ts +++ b/packages/cli/src/api/sync.ts @@ -1,7 +1,13 @@ import { requestData } from "./request" -export async function initApi(appid: string) { +/** + * 根据 appid 同步数据 + * @param {string} appid + * @returns + */ + +export async function syncApp(appid: string) { const url = `/sys-api/apps/${appid}/export` const obj = { diff --git a/packages/cli/src/api/user.ts b/packages/cli/src/api/user.ts index 526658defd..ad7414a48c 100644 --- a/packages/cli/src/api/user.ts +++ b/packages/cli/src/api/user.ts @@ -1,8 +1,15 @@ import axios from 'axios' +/** + * 根据 server 登陆 + * @param {string} server + * @param {Object} obj + * @returns + */ + export async function loginApi(server:string,obj:Object) { // remote server login url - const url = `${server}/sys-extension-api/func/password-login`; + const url = `${server}/sys-api/account/login`; try{ diff --git a/packages/cli/src/apps.ts b/packages/cli/src/apps.ts index e3b4b9b3e0..75d5a2c10b 100644 --- a/packages/cli/src/apps.ts +++ b/packages/cli/src/apps.ts @@ -1,13 +1,13 @@ import { program } from 'commander' import { appStop, appStart, appRestart } from './api/apps' -import { appListCommand } from './actions/appAction' +import { handleAppListCommand } from './actions/appAction' program .command('list') .action(async () => { - await appListCommand() + await handleAppListCommand() }) diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts deleted file mode 100644 index 1030888e81..0000000000 --- a/packages/cli/src/config/config.ts +++ /dev/null @@ -1,13 +0,0 @@ - -import * as path from 'node:path' -import {homedir} from 'node:os' - -const credentials_dir = ".laf-credentials" - -const auth_file = "auth.json" - -export const API_BASE_URL = "https://console.lafyun.com/" -export const CREDENTIALS_DIR = path.resolve(homedir(),credentials_dir) -export const AUTH_FILE = path.resolve(CREDENTIALS_DIR,auth_file) - -export const LAF_FILE = "laf.json" \ No newline at end of file diff --git a/packages/cli/src/functions.ts b/packages/cli/src/functions.ts index 2e39d61a00..c0d7fbf640 100644 --- a/packages/cli/src/functions.ts +++ b/packages/cli/src/functions.ts @@ -1,65 +1,75 @@ import { program } from 'commander' -import * as path from 'node:path' -import * as fs from 'node:fs' -import { pullFunction } from './api/functions' - -import { LAF_FILE , FUNCTIONS_DIR ,FUNCTIONS_FILE} from './utils/constants' -import { checkDir } from './utils/util' +import { pullFunction ,getFunctionByName ,publishFunction } from './api/functions' +import { getAppData,checkFuncNameDir } from './utils/util' +import { handlePullFunctionCommand,handleInvokeFunctionCommand ,handlePushFunctionCommand} from './actions/functionAction' -const appFile = path.resolve(process.cwd(), LAF_FILE) -const appData = JSON.parse(fs.readFileSync(appFile, 'utf8')) +const appData = getAppData() program .command('fn-pull') .argument('[function-name]',"functionname") .option('-f, --force-overwrite', 'force to file ignore if modified', false) .action(async ( functionName,options) => { - // pull function const response =await pullFunction(appData.appid,functionName) - if(response.total){ + await handlePullFunctionCommand(response.data,options) + }else{ + console.log('functions not find') + } +}) - // functions dir - const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) +program +.command('fn-invoke') +.argument('function-name',"functionname") +.argument('[param]','function param','{}') +.action(async ( functionName,param) => { - checkDir(functionsDir) + try{ + const debugParams= JSON.parse(param) + // check function + checkFuncNameDir(functionName) + await handleInvokeFunctionCommand(appData.appid,functionName,debugParams) - response.data.forEach(element => { + }catch(err){ + console.error(err.message) + process.exit(1) - //fuction name - const funcName =element.name; - const funcNameDir = path.resolve(functionsDir, funcName) - - checkDir(funcNameDir) - - const funcFile= path.resolve(funcNameDir, FUNCTIONS_FILE) - try{ - // check if exist function file - fs.accessSync(funcFile) - const currentCode =fs.readFileSync(funcFile,'utf-8') - - if(currentCode){ - // forceOverwrite - if(options.forceOverwrite){ - fs.writeFileSync(funcFile, element.code) - } - }else{ - fs.writeFileSync(funcFile, element.code) - } - }catch(err){ - - fs.writeFileSync(funcFile, element.code) + } - } - - console.log('pull success') - }) +}) - }else{ - console.log('functions not find') +program +.command('fn-push') +.argument('function-name',"functionname") +.option('-f, --force-overwrite', 'force to file ignore if modified', false) +.action(async ( functionName,options) => { + // check fucntion + checkFuncNameDir(functionName) + + await handlePushFunctionCommand(appData.appid,functionName,options) + +}) + + +program +.command('fn-publish') +.argument('function-name',"functionname") +.action(async ( functionName) => { + + // get function + const record = await getFunctionByName(appData.appid,functionName) + + if(record.data){ + // publish function + const res = await publishFunction(appData.appid,functionName) + if(res.code==0){ + console.log('publish success') + } + }else{ + console.log('funtion not exist') } }) diff --git a/packages/cli/src/init.ts b/packages/cli/src/init.ts index e83766e018..ab6fc97573 100644 --- a/packages/cli/src/init.ts +++ b/packages/cli/src/init.ts @@ -1,53 +1,26 @@ import { program } from 'commander' -import { initApi } from './api/init' -import * as fs from 'node:fs' -import * as AdmZip from 'adm-zip' -import * as path from 'node:path' - -import { LAF_FILE } from './utils/constants' - -import { checkDir } from './utils/util' +import { syncApp } from './api/sync' +import { getApplicationByAppid } from './api/apps' +import { handleInitAppCommand ,handleSyncAppCommand} from './actions/initActiont' program .command('init ') .option('-s, --sync', 'sync app', false) .action(async (appid, options) => { - try { - // get app - const result = await initApi(appid) - - // get app name - const appName = result.headers['content-disposition'].slice(22, -5) - - const appPath = path.resolve(process.cwd(), appName) - - checkDir(appPath) - const lafFile = path.resolve(appName, LAF_FILE) - - // write data - fs.writeFileSync(lafFile, JSON.stringify({ appid: appid, root: "@laf" ,endPoint:""})) - - console.log('save success') + const result = await getApplicationByAppid(appid) + const appName = result.data.application.name + + await handleInitAppCommand(appName,appid,result.data.app_deploy_host) // sync app data if (options.sync) { - - // add app data to zip - const appZip = appName + '.zip' - const appZipPath = path.resolve(process.cwd(), appZip) - const writer = fs.createWriteStream(appZipPath) - result.data.pipe(writer) - await new Promise((resolve, reject) => { - writer.on('finish', resolve) - writer.on('error', reject) - }) - - // unzip - const file = new AdmZip(appZipPath) - file.extractAllTo(appPath) + //sync app + const data = await syncApp(appid) + + await handleSyncAppCommand(appName,data) } } catch (err) { diff --git a/packages/cli/src/utils/tokens.ts b/packages/cli/src/utils/tokens.ts index 90fb46cba4..dab73086b9 100644 --- a/packages/cli/src/utils/tokens.ts +++ b/packages/cli/src/utils/tokens.ts @@ -1,7 +1,14 @@ import * as fs from 'node:fs' +import * as path from 'node:path' +import { CREDENTIALS_DIR, AUTH_FILE,LAF_FILE } from '../utils/constants' +import { getApplicationByAppid } from '../api/apps' -import { CREDENTIALS_DIR, AUTH_FILE } from '../utils/constants' + +/** + * get access token + * @returns + */ export async function getAccessToken() { @@ -9,7 +16,7 @@ export async function getAccessToken() { // check dir fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK | fs.constants.W_OK) } catch (err) { - console.error("please login first") + console.error("please login first 2") process.exit(1) } @@ -26,4 +33,16 @@ export async function getAccessToken() { return authData.access_token } -// debug token + +/** + * get debug token + * @returns + */ +export async function getDebugToken() { + + const appFile = path.resolve(process.cwd(), LAF_FILE) + const appData = JSON.parse(fs.readFileSync(appFile, 'utf8')) + const response = await getApplicationByAppid(appData.appid); + return response.data.debug_token + +} diff --git a/packages/cli/src/utils/util-lang.ts b/packages/cli/src/utils/util-lang.ts new file mode 100644 index 0000000000..03184f7777 --- /dev/null +++ b/packages/cli/src/utils/util-lang.ts @@ -0,0 +1,18 @@ + + +import * as ts from 'typescript' + + +/** + * compile typescript code to javascript + * @param source typescript source code + */ +export function compileTs2js(source: string) { + const jscode = ts.transpile(source, { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2017, + removeComments: true, + }) + + return jscode +} \ No newline at end of file diff --git a/packages/cli/src/utils/util.ts b/packages/cli/src/utils/util.ts index 1fcce32a66..c6029f7794 100644 --- a/packages/cli/src/utils/util.ts +++ b/packages/cli/src/utils/util.ts @@ -1,9 +1,11 @@ import * as fs from 'node:fs' import * as path from 'node:path' -import {CREDENTIALS_DIR} from './constants' -import {AUTH_FILE} from '../utils/constants' +import { CREDENTIALS_DIR } from './constants' +import { AUTH_FILE,LAF_FILE,FUNCTIONS_DIR } from '../utils/constants' -// check auth dir +/** + * check auth dir + */ export function checkCredentialsDir(){ try{ @@ -16,6 +18,10 @@ export function checkCredentialsDir(){ } +/** + * check dir + * @param {string} dir + */ export function checkDir(dir:string){ try{ // check dir @@ -27,20 +33,11 @@ export function checkDir(dir:string){ } -export function getRemoteServe(){ - - try{ - // check dir - fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK|fs.constants.W_OK) - }catch(err){ - console.error("please login first") - process.exit(1) - } - - const authData = JSON.parse(fs.readFileSync(AUTH_FILE, 'utf8')); - return authData.remote -} +/** + * get all funtions in app functions dir + * @returns + */ export function getAppFunctions(dir:string){ let arrFiles = [] @@ -59,4 +56,64 @@ export function getAppFunctions(dir:string){ return arrFiles; } + +/** + * check dir + * @param {string} functionName + */ + +export function checkFuncNameDir(functionName:string){ + + const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) + checkDir(functionsDir) + const functions = getAppFunctions(functionsDir) + if(functionName){ + if(!functions.includes(functionName)){ + console.error('function not exist') + process.exit(1) + } + } + +} + + +/** + * get remote server in AUTH_FILE + * @returns + */ + +export function getRemoteServe(){ + + try{ + // check dir + fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK|fs.constants.W_OK) + }catch(err){ + console.error("please login first 3") + process.exit(1) + } + const authData = JSON.parse(fs.readFileSync(AUTH_FILE, 'utf8')); + return authData.remote +} + + +/** + * get app data in LAF_FILE + * @returns + */ + +export function getAppData(){ + + try{ + const appFile = path.resolve(process.cwd(), LAF_FILE) + // check file + fs.accessSync(appFile, fs.constants.R_OK|fs.constants.W_OK) + const appData = JSON.parse(fs.readFileSync(appFile, 'utf8')) + return appData + }catch(err){ + console.error("cant find laf.json") + process.exit(1) + } + +} + \ No newline at end of file From ca47f4fee63ad5fa3788a79d8154d911020ff74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Mon, 27 Jun 2022 08:58:38 +0800 Subject: [PATCH 10/12] add server api --- .../system-server/src/handler/function/get.ts | 21 ++++++- .../src/handler/function/index.ts | 26 ++++++-- .../src/handler/function/publish.ts | 30 ++++++++- .../src/handler/function/update.ts | 61 ++++++++++++++++++- .../system-server/src/support/function.ts | 31 ++++++++++ 5 files changed, 162 insertions(+), 7 deletions(-) diff --git a/packages/system-server/src/handler/function/get.ts b/packages/system-server/src/handler/function/get.ts index 6c4659d549..2f372a8e86 100644 --- a/packages/system-server/src/handler/function/get.ts +++ b/packages/system-server/src/handler/function/get.ts @@ -8,7 +8,7 @@ import { Request, Response } from 'express' import { ObjectId } from 'mongodb' import { IApplicationData, getApplicationDbAccessor } from '../../support/application' -import { ICloudFunctionData, getFunctionById } from '../../support/function' +import { ICloudFunctionData, getFunctionById ,getFunctionByName} from '../../support/function' import { checkPermission } from '../../support/permission' import { CN_ACCOUNTS, CN_FUNCTIONS, CN_FUNCTION_HISTORY, CN_PUBLISHED_FUNCTIONS } from '../../constants' import { FunctionActionDef } from '../../actions' @@ -96,6 +96,25 @@ export async function handleGetFunctionById(req: Request, res: Response) { } +/** + * Get a function by name + */ + +export async function handleGetFunctionByName(req: Request, res: Response) { + const app: IApplicationData = req['parsed-app'] + const func_name = req.params.func_name + + // check permission + const code = await checkPermission(req['auth']?.uid, FunctionActionDef.GetFunction, app) + if (code) { + return res.status(code).send() + } + + const doc = await getFunctionByName(app.appid, func_name) + + return res.send({ data: doc }) +} + /** * Get all of the function tags */ diff --git a/packages/system-server/src/handler/function/index.ts b/packages/system-server/src/handler/function/index.ts index df41383e09..0d5fbb63a2 100644 --- a/packages/system-server/src/handler/function/index.ts +++ b/packages/system-server/src/handler/function/index.ts @@ -7,12 +7,12 @@ import { Router } from "express" import { handleCreateFunction } from "./create" -import { handleGetAllFunctionTags, handleGetFunctionById, handleGetFunctionHistory, handleGetFunctions, handleGetPublishedFunctions } from "./get" +import { handleGetAllFunctionTags, handleGetFunctionById,handleGetFunctionByName, handleGetFunctionHistory, handleGetFunctions, handleGetPublishedFunctions } from "./get" import { handleGetFunctionLogs } from "./logs" -import { handlePublishFunctions, handlePublishOneFunction } from "./publish" +import { handlePublishFunctions, handlePublishOneFunction, handlePublishOneFunctionByName } from "./publish" import { handleRemoveFunctionById } from "./remove" import { handleCreateTrigger, handleRemoveTrigger, handleUpdateTrigger } from "./trigger" -import { handleCompileFunctionCode, handleUpdateFunction, handleUpdateFunctionCode } from "./update" +import { handleCompileFunctionCode, handleUpdateFunction, handleUpdateFunctionCode,handleUpdateFunctionCodeByName } from "./update" export const FunctionRouter = Router() @@ -102,4 +102,22 @@ FunctionRouter.post('/:func_id/triggers/:trigger_id', handleUpdateTrigger) /** * Remove a trigger of the function */ -FunctionRouter.delete('/:func_id/triggers/:trigger_id', handleRemoveTrigger) \ No newline at end of file +FunctionRouter.delete('/:func_id/triggers/:trigger_id', handleRemoveTrigger) + + +/** + * get function by name + */ + FunctionRouter.get('/getFunction/:func_name', handleGetFunctionByName) + + + /** + * update function by name + */ + FunctionRouter.post('/updateFunction/:func_name', handleUpdateFunctionCodeByName) + + +/** + * publish function by name + */ +FunctionRouter.post('/publishFunction/:func_name', handlePublishOneFunctionByName) \ No newline at end of file diff --git a/packages/system-server/src/handler/function/publish.ts b/packages/system-server/src/handler/function/publish.ts index c335f11a20..68df1605d2 100644 --- a/packages/system-server/src/handler/function/publish.ts +++ b/packages/system-server/src/handler/function/publish.ts @@ -7,7 +7,7 @@ import { Request, Response } from 'express' import { IApplicationData } from '../../support/application' -import { publishFunctions, publishOneFunction } from '../../support/function' +import { publishFunctions, publishOneFunction, publishOneFunctionByName } from '../../support/function' import { checkPermission } from '../../support/permission' import Config from '../../config' import { FunctionActionDef } from '../../actions' @@ -67,3 +67,31 @@ export async function handlePublishOneFunction(req: Request, res: Response) { return res.status(500).send(Config.isProd ? undefined : error) } } + + +/** + * Publish one function by name + */ + export async function handlePublishOneFunctionByName(req: Request, res: Response) { + const uid = req['auth']?.uid + const func_name = req.params?.func_name + const app: IApplicationData = req['parsed-app'] + + // check permission + const code = await checkPermission(uid, FunctionActionDef.PublishFunction, app) + if (code) { + return res.status(code).send() + } + + try { + await publishOneFunctionByName(app, func_name) + + return res.send({ + code: 0, + data: 'ok' + }) + } catch (error) { + logger.error(`public functions occurred error`, error) + return res.status(500).send(Config.isProd ? undefined : error) + } +} diff --git a/packages/system-server/src/handler/function/update.ts b/packages/system-server/src/handler/function/update.ts index 4f6b479a00..76e77b2f68 100644 --- a/packages/system-server/src/handler/function/update.ts +++ b/packages/system-server/src/handler/function/update.ts @@ -9,7 +9,7 @@ import { Request, Response } from 'express' import { ObjectId } from 'mongodb' import { IApplicationData } from '../../support/application' -import { getFunctionById } from '../../support/function' +import { getFunctionById, getFunctionByName } from '../../support/function' import { checkPermission } from '../../support/permission' import { CN_FUNCTIONS, CN_FUNCTION_HISTORY } from '../../constants' import { FunctionActionDef } from '../../actions' @@ -140,6 +140,65 @@ export async function handleUpdateFunctionCode(req: Request, res: Response) { return res.send({ data: doc }) } + +/** + * Update function's code + */ + export async function handleUpdateFunctionCodeByName(req: Request, res: Response) { + const uid = req['auth']?.uid + const db = DatabaseAgent.db + const app: IApplicationData = req['parsed-app'] + const func_name = req.params.func_name + + // check permission + const code = await checkPermission(uid, FunctionActionDef.UpdateFunction, app) + if (code) { + return res.status(code).send() + } + + const body = req.body + if (!body.code) return res.status(422).send('code cannot be empty') + + const func = await getFunctionByName(app.appid, func_name) + if (!func) return res.status(422).send('function not found') + + // build the func data + const data = { + code: body.code, + compiledCode: compileTs2js(body.code), + hash: hashFunctionCode(body.code), + debugParams: body.debugParams || func.debugParams, + updated_at: new Date() + } + + // update cloud function + await db.collection(CN_FUNCTIONS) + .updateOne({ + _id: func._id, + appid: app.appid + }, { + $set: data, + $inc: { version: 1 } + }) + + const doc = await getFunctionById(app.appid, func._id) + + // record the function change history + if (doc.hash !== func.hash) { + const record = Object.assign({}, doc) + await db.collection(CN_FUNCTION_HISTORY) + .insertOne({ + appid: app.appid, + func_id: func._id, + data: record, + created_at: new Date(), + created_by: new ObjectId(uid), + }) + } + + return res.send({ data: doc }) +} + /** * Compile function's code */ diff --git a/packages/system-server/src/support/function.ts b/packages/system-server/src/support/function.ts index 2991517a06..447bff9c6c 100644 --- a/packages/system-server/src/support/function.ts +++ b/packages/system-server/src/support/function.ts @@ -141,6 +141,37 @@ export async function publishOneFunction(app: IApplicationData, func_id: string) } } + +/** + * Publish one function + * Means that copying sys db function to app db + */ + export async function publishOneFunctionByName(app: IApplicationData, func_name: string) { + + // read functions from sys db + const func = await getFunctionByName(app.appid, func_name) + + // compile functions + compileFunction(func) + + // write function to app db + const app_accessor = await getApplicationDbAccessor(app) + const session = app_accessor.conn.startSession() + + try { + await session.withTransaction(async () => { + const _db = app_accessor.db + const app_coll = _db.collection(CN_PUBLISHED_FUNCTIONS) + await app_coll.deleteOne({ _id: func._id }, { session }) + await app_coll.insertOne(func as any, { session }) + }) + } catch (error) { + logger.error(error) + } finally { + await session.endSession() + } +} + /** * Compile function codes (from typescript to javascript) * @param func From 089c4f123bc3af841813f8290949f02a29aab3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Mon, 27 Jun 2022 15:53:59 +0800 Subject: [PATCH 11/12] add command to index --- .../cli/src/actions/{appAction.ts => app.ts} | 0 .../{functionAction.ts => function.ts} | 83 +++++---- .../src/actions/{initActiont.ts => init.ts} | 5 +- .../src/actions/{userAction.ts => user.ts} | 0 packages/cli/src/api/functions.ts | 16 +- packages/cli/src/apps.ts | 58 ------ packages/cli/src/functions.ts | 132 +++++++------- packages/cli/src/index.ts | 167 ++++++++++-------- packages/cli/src/init.ts | 32 ---- packages/cli/src/login.ts | 30 ---- .../src/handler/function/index.ts | 6 +- 11 files changed, 221 insertions(+), 308 deletions(-) rename packages/cli/src/actions/{appAction.ts => app.ts} (100%) rename packages/cli/src/actions/{functionAction.ts => function.ts} (67%) rename packages/cli/src/actions/{initActiont.ts => init.ts} (93%) rename packages/cli/src/actions/{userAction.ts => user.ts} (100%) delete mode 100644 packages/cli/src/apps.ts delete mode 100644 packages/cli/src/init.ts delete mode 100644 packages/cli/src/login.ts diff --git a/packages/cli/src/actions/appAction.ts b/packages/cli/src/actions/app.ts similarity index 100% rename from packages/cli/src/actions/appAction.ts rename to packages/cli/src/actions/app.ts diff --git a/packages/cli/src/actions/functionAction.ts b/packages/cli/src/actions/function.ts similarity index 67% rename from packages/cli/src/actions/functionAction.ts rename to packages/cli/src/actions/function.ts index ca4065c0a1..e3863cd469 100644 --- a/packages/cli/src/actions/functionAction.ts +++ b/packages/cli/src/actions/function.ts @@ -19,41 +19,41 @@ import { debugFunction,getFunctionByName,pushFunction,createFunction} from '../a export async function handlePullFunctionCommand(data:any,options:any) { - // functions dir - const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) - - checkDir(functionsDir) - - data.forEach(element => { - - //fuction name - const funcName =element.name; - const funcNameDir = path.resolve(functionsDir, funcName) - - checkDir(funcNameDir) - - const funcFile= path.resolve(funcNameDir, FUNCTIONS_FILE) - try{ - // check if exist function file - fs.accessSync(funcFile) - const currentCode =fs.readFileSync(funcFile,'utf-8') - - if(currentCode){ - // forceOverwrite - if(options.forceOverwrite){ - fs.writeFileSync(funcFile, element.code) - } - }else{ - fs.writeFileSync(funcFile, element.code) - } - }catch(err){ - - fs.writeFileSync(funcFile, element.code) - - } - - console.log('pull success') - }) + // functions dir + const functionsDir = path.resolve(process.cwd(), FUNCTIONS_DIR) + + checkDir(functionsDir) + + data.forEach(element => { + + //fuction name + const funcName =element.name; + const funcNameDir = path.resolve(functionsDir, funcName) + + checkDir(funcNameDir) + + const funcFile= path.resolve(funcNameDir, FUNCTIONS_FILE) + try{ + // check if exist function file + fs.accessSync(funcFile) + const currentCode =fs.readFileSync(funcFile,'utf-8') + + if(currentCode){ + // forceOverwrite + if(options.forceOverwrite){ + fs.writeFileSync(funcFile, element.code) + } + }else{ + fs.writeFileSync(funcFile, element.code) + } + }catch(err){ + + fs.writeFileSync(funcFile, element.code) + + } + + console.log('pull success') + }) } @@ -62,6 +62,7 @@ export async function handlePullFunctionCommand(data:any,options:any) { * invoke function * @param {string} appid * @param {string} functionName + * @param {object} param * @returns */ @@ -72,7 +73,7 @@ export async function handleInvokeFunctionCommand(appid:string,functionName:stri // get local code const functionNameDir = path.resolve(functionsDir, functionName) const funcFile= path.resolve(functionNameDir, FUNCTIONS_FILE) - const code = fs.readFileSync(funcFile, 'utf8') + const code = fs.readFileSync(funcFile, 'utf8') const obj = { func:{ appid: appid, @@ -84,9 +85,9 @@ export async function handleInvokeFunctionCommand(appid:string,functionName:stri param:param } - const res = await debugFunction(appid,'test',obj) + const res = await debugFunction(functionName,obj) console.log(res) - + } @@ -124,9 +125,12 @@ export async function handlePushFunctionCommand(appid:string,functionName:string if(res.data){ console.log("push success") } + }else{ + + console.log("romote code is different with local") } }else{ - console.log("push success1") + console.log("romote code is same with local") } }else{ @@ -135,6 +139,7 @@ export async function handlePushFunctionCommand(appid:string,functionName:string code:code, name:functionName, label:"test", + status:1 } const res = await createFunction(appid,data) diff --git a/packages/cli/src/actions/initActiont.ts b/packages/cli/src/actions/init.ts similarity index 93% rename from packages/cli/src/actions/initActiont.ts rename to packages/cli/src/actions/init.ts index 001c67c5c5..69686cbbef 100644 --- a/packages/cli/src/actions/initActiont.ts +++ b/packages/cli/src/actions/init.ts @@ -9,7 +9,7 @@ import { pipeline } from 'node:stream/promises' /** * init app - * @param {string} funcName + * @param {string} appName * @param {string} appid * @param {string} endPoint * @returns @@ -28,7 +28,7 @@ export async function handleInitAppCommand(appName:string,appid:string,endPoint: /** * sync app - * @param {string} funcName + * @param {string} appName * @param {any} data * @returns */ @@ -47,5 +47,6 @@ export async function handleSyncAppCommand(appName:string,data:any) { file.extractAllTo(appPath) fs.unlinkSync(appZipPath) + console.log('success') } \ No newline at end of file diff --git a/packages/cli/src/actions/userAction.ts b/packages/cli/src/actions/user.ts similarity index 100% rename from packages/cli/src/actions/userAction.ts rename to packages/cli/src/actions/user.ts diff --git a/packages/cli/src/api/functions.ts b/packages/cli/src/api/functions.ts index 8c78055b37..0288b2cfdc 100644 --- a/packages/cli/src/api/functions.ts +++ b/packages/cli/src/api/functions.ts @@ -32,17 +32,16 @@ export async function pullFunction(appid: string,functionName:string) { /** * 调试函数 - * @param {string} appid * @param {string} functionName * @param {Object} obj * @returns */ -export async function debugFunction(appid: string,functionName:string,obj:Object) { +export async function debugFunction(functionName:string,obj:Object) { const appData = getAppData(); - const url = `http://${appid}.${appData.endPoint}/debug/${functionName}` + const url = `${appData.endPoint}/debug/${functionName}` try{ @@ -52,7 +51,10 @@ export async function debugFunction(appid: string,functionName:string,obj:Object const result = await axios.post(url,obj,{headers:headers}) const response = result.data - + if(response.error){ + console.error(response.error) + process.exit(1) + } return response }catch(err){ @@ -94,7 +96,7 @@ export async function createFunction(appid: string,data:Object) { */ export async function getFunctionByName(appid: string,functionName:string) { - const url= `/sys-api/apps/${appid}/function/getFunction/${functionName}` + const url= `/sys-api/apps/${appid}/function/detail/${functionName}` const obj = { method: "GET", url, @@ -113,7 +115,7 @@ export async function getFunctionByName(appid: string,functionName:string) { export async function pushFunction(appid: string,functionName:string,data:object) { - const url= `/sys-api/apps/${appid}/function/updateFunction/${functionName}` + const url= `/sys-api/apps/${appid}/function/save/${functionName}` const obj = { method: "POST", @@ -134,7 +136,7 @@ export async function pushFunction(appid: string,functionName:string,data:object */ export async function publishFunction(appid: string,functionName:string) { - const url= `/sys-api/apps/${appid}/function/publishFunction/${functionName}` + const url= `/sys-api/apps/${appid}/function/publish/${functionName}` const obj = { method: "POST", diff --git a/packages/cli/src/apps.ts b/packages/cli/src/apps.ts deleted file mode 100644 index 75d5a2c10b..0000000000 --- a/packages/cli/src/apps.ts +++ /dev/null @@ -1,58 +0,0 @@ - -import { program } from 'commander' -import { appStop, appStart, appRestart } from './api/apps' -import { handleAppListCommand } from './actions/appAction' - -program - .command('list') - .action(async () => { - - await handleAppListCommand() - - }) - -program - .command('stop ') - .option('--env ', `the file name to generate`, '.env') - .action(async (appid) => { - - const response = await appStop(appid) - - if (response.data.result) { - console.log('stop success') - } else { - console.log('stop failed') - } - }) - - -program - .command('start ') - .option('--env ', `the file name to generate`, '.env') - .action(async (appid) => { - - const response = await appStart(appid) - - if (response.data.result) { - console.log('start success') - } else { - console.log('start failed') - } - }) - - -program - .command('restart ') - .option('--env ', `the file name to generate`, '.env') - .action(async (appid) => { - - const response = await appRestart(appid) - - if (response.data.result) { - console.log('restart success') - } else { - console.log('restart failed') - } - }) - -program.parse(process.argv) diff --git a/packages/cli/src/functions.ts b/packages/cli/src/functions.ts index c0d7fbf640..5ec35a7d5d 100644 --- a/packages/cli/src/functions.ts +++ b/packages/cli/src/functions.ts @@ -1,79 +1,89 @@ -import { program } from 'commander' +import { Command } from 'commander' import { pullFunction ,getFunctionByName ,publishFunction } from './api/functions' import { getAppData,checkFuncNameDir } from './utils/util' -import { handlePullFunctionCommand,handleInvokeFunctionCommand ,handlePushFunctionCommand} from './actions/functionAction' - -const appData = getAppData() - -program -.command('fn-pull') -.argument('[function-name]',"functionname") -.option('-f, --force-overwrite', 'force to file ignore if modified', false) -.action(async ( functionName,options) => { - // pull function - const response =await pullFunction(appData.appid,functionName) - if(response.total){ - await handlePullFunctionCommand(response.data,options) - }else{ - console.log('functions not find') - } -}) - -program -.command('fn-invoke') -.argument('function-name',"functionname") -.argument('[param]','function param','{}') -.action(async ( functionName,param) => { - - try{ - const debugParams= JSON.parse(param) - // check function - checkFuncNameDir(functionName) - await handleInvokeFunctionCommand(appData.appid,functionName,debugParams) +import { handlePullFunctionCommand,handleInvokeFunctionCommand ,handlePushFunctionCommand} from './actions/function' + +export function makeFnCommand(){ + + const fn = new Command('fn'); + + fn + .command('pull') + .argument('[function-name]',"functionname") + .option('-f, --force-overwrite', 'force to file ignore if modified', false) + .action(async ( functionName,options) => { + + const appData = getAppData() + // pull function + const response =await pullFunction(appData.appid,functionName) + if(response.total){ + await handlePullFunctionCommand(response.data,options) + }else{ + console.log('functions not find') + } + }) - }catch(err){ - console.error(err.message) - process.exit(1) + fn + .command('invoke') + .argument('function-name',"functionname") + .argument('[param]','function param','{}') + .action(async ( functionName,param) => { + const appData = getAppData() - } - -}) + try{ + const debugParams= JSON.parse(param) + // check function + checkFuncNameDir(functionName) + await handleInvokeFunctionCommand(appData.appid,functionName,debugParams) + + }catch(err){ + console.error(err.message) + process.exit(1) + + } + + }) + + + fn + .command('push') + .argument('function-name',"functionname") + .option('-f, --force-overwrite', 'force to file ignore if modified', false) + .action(async ( functionName,options) => { + const appData = getAppData() + // check fucntion + checkFuncNameDir(functionName) -program -.command('fn-push') -.argument('function-name',"functionname") -.option('-f, --force-overwrite', 'force to file ignore if modified', false) -.action(async ( functionName,options) => { - // check fucntion - checkFuncNameDir(functionName) + await handlePushFunctionCommand(appData.appid,functionName,options) - await handlePushFunctionCommand(appData.appid,functionName,options) + }) -}) + fn + .command('publish') + .argument('function-name',"functionname") + .action(async ( functionName) => { -program -.command('fn-publish') -.argument('function-name',"functionname") -.action(async ( functionName) => { + const appData = getAppData() - // get function - const record = await getFunctionByName(appData.appid,functionName) + // get function + const record = await getFunctionByName(appData.appid,functionName) - if(record.data){ - // publish function - const res = await publishFunction(appData.appid,functionName) - if(res.code==0){ - console.log('publish success') + if(record.data){ + // publish function + const res = await publishFunction(appData.appid,functionName) + if(res.code==0){ + console.log('publish success') + } + }else{ + console.log('funtion not exist') } - }else{ - console.log('funtion not exist') - } -}) + }) + return fn; -program.parse(process.argv); +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index a93c881312..fddff2322d 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,86 +1,18 @@ #!/usr/bin/env node import { program } from 'commander' -import * as dotenv from 'dotenv' -import { resolve } from 'node:path' -import { handleSyncCommand } from './sync' -import { handleLoginCommand } from './actions/userAction' -import * as fs from 'node:fs' -// laf-cli sync -program - .command('sync ') - .description(`sync files to bucket`) - .option('-e, --endpoint ', 'oss endpoint, default is https://oss.lafyun.com, could be override by env OSS_ENDPOINT') - .option('-k, --access-key ', 'app oss access-key, default is env OSS_ACCESS_KEY') - .option('-s, --access-secret ', 'app oss access-secret, default is env OSS_ACCESS_SECRET') - .option('-b, --bucket-name ', 'bucket-name, default is env OSS_BUCKET_NAME') - .option('-r, --region ', 'region', 'cn-default') - .option('-d, --dry-run', 'dry-run mode', false) - .option('-f, --force', 'force to updated all files ignore if modified', false) - .option('--env ', `your can specify a env file`, '.env') - .action(async (source, options) => { - dotenv.config({ path: resolve(process.cwd(), options.env) }) - - const endpoint = options.endpoint || process.env.OSS_ENDPOINT || 'https://oss.lafyun.com' - const accessKey = options.accessKey || process.env.OSS_ACCESS_KEY - const accessSecret = options.accessSecret || process.env.OSS_ACCESS_SECRET - const bucketName = options.bucketName || process.env.OSS_BUCKET_NAME - const region = options.region || process.env.OSS_REGION - const dryRun = options.dryRun || false - const force = options.force || false - - if(!endpoint) { - console.error('endpoint is required') - process.exit(1) - } +import { handleLoginCommand } from './actions/user' - if(!accessKey) { - console.error('accessKey is required') - process.exit(1) - } +import { syncApp } from './api/sync' +import { getApplicationByAppid } from './api/apps' +import { handleInitAppCommand ,handleSyncAppCommand} from './actions/init' - if(!accessSecret) { - console.error('accessSecret is required') - process.exit(1) - } +import { appStop, appStart, appRestart } from './api/apps' +import { handleAppListCommand } from './actions/app' - if(!bucketName) { - console.error('bucketName is required') - process.exit(1) - } - - await handleSyncCommand(source, { endpoint, accessKey, accessSecret, bucketName, dryRun, force, region }) - }) +import { makeFnCommand} from './functions' -program - .command('init') - .description('generate or update .env file') - .option('-e, --endpoint ', 'oss endpoint, default is https://oss.lafyun.com, could be override by env OSS_ENDPOINT') - .option('-k, --access-key ', 'app oss access-key, default is env OSS_ACCESS_KEY') - .option('-s, --access-secret ', 'app oss access-secret, default is env OSS_ACCESS_SECRET') - .option('-b, --bucket-name ', 'bucket-name, default is env OSS_BUCKET_NAME') - .option('-r, --region ', 'region', 'cn-default') - .option('--env ', `the file name to generate`, '.env') - .action(async (options) => { - const envFile = resolve(process.cwd(), options.env) - dotenv.config({ path: envFile}) - - const endpoint = options.endpoint || process.env.OSS_ENDPOINT || 'https://oss.lafyun.com' - const accessKey = options.accessKey || process.env.OSS_ACCESS_KEY || '' - const accessSecret = options.accessSecret || process.env.OSS_ACCESS_SECRET || '' - const bucketName = options.bucketName || process.env.OSS_BUCKET_NAME || '' - const region = options.region || process.env.OSS_REGION - - const content = `OSS_ENDPOINT=${endpoint} -OSS_ACCESS_KEY=${accessKey} -OSS_ACCESS_SECRET=${accessSecret} -OSS_BUCKET_NAME=${bucketName} -OSS_REGION=${region}` - - fs.writeFileSync(envFile, content) - console.log(`Generated: ${envFile}`) - }) program @@ -95,7 +27,7 @@ program console.log(' npm install -g laf-cli') }) - program +program .command('login') .option('-u, --username ', 'username') .option('-p, --password ', 'password') @@ -120,5 +52,88 @@ program }) + +program + .command('init ') + .option('-s, --sync', 'sync app', false) + .action(async (appid, options) => { + try { + // get app + const result = await getApplicationByAppid(appid) + const appName = result.data.application.name + const endPoint = `${result.data.app_deploy_url_schema}://${appid}.${result.data.app_deploy_host}` + + await handleInitAppCommand(appName,appid,endPoint) + + // sync app data + if (options.sync) { + //sync app + const data = await syncApp(appid) + + await handleSyncAppCommand(appName,data) + } + } + catch (err) { + console.log(err.message) + } + + }) + + +program + .command('list') + .action(async () => { + + await handleAppListCommand() + + }) + +program + .command('stop ') + .option('--env ', `the file name to generate`, '.env') + .action(async (appid) => { + + const response = await appStop(appid) + + if (response.data.result) { + console.log('stop success') + } else { + console.log('stop failed') + } + }) + + +program + .command('start ') + .option('--env ', `the file name to generate`, '.env') + .action(async (appid) => { + + const response = await appStart(appid) + + if (response.data.result) { + console.log('start success') + } else { + console.log('start failed') + } + }) + + +program + .command('restart ') + .option('--env ', `the file name to generate`, '.env') + .action(async (appid) => { + + const response = await appRestart(appid) + + if (response.data.result) { + console.log('restart success') + } else { + console.log('restart failed') + } + }) + +program.addCommand(makeFnCommand()) + + program.parse(process.argv) diff --git a/packages/cli/src/init.ts b/packages/cli/src/init.ts deleted file mode 100644 index ab6fc97573..0000000000 --- a/packages/cli/src/init.ts +++ /dev/null @@ -1,32 +0,0 @@ - -import { program } from 'commander' -import { syncApp } from './api/sync' -import { getApplicationByAppid } from './api/apps' -import { handleInitAppCommand ,handleSyncAppCommand} from './actions/initActiont' - -program - .command('init ') - .option('-s, --sync', 'sync app', false) - .action(async (appid, options) => { - try { - // get app - const result = await getApplicationByAppid(appid) - const appName = result.data.application.name - - await handleInitAppCommand(appName,appid,result.data.app_deploy_host) - - // sync app data - if (options.sync) { - //sync app - const data = await syncApp(appid) - - await handleSyncAppCommand(appName,data) - } - } - catch (err) { - console.log(err.message) - } - - }) - -program.parse(process.argv) diff --git a/packages/cli/src/login.ts b/packages/cli/src/login.ts deleted file mode 100644 index 80c3b5b1de..0000000000 --- a/packages/cli/src/login.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import { program } from 'commander' - -import { handleLoginCommand } from './actions/userAction' - -program - .command('login') - .option('-u, --username ', 'username') - .option('-p, --password ', 'password') - .option('-r, --remote ', 'remote server', "https://console.lafyun.com") - .action(async (options) => { - - // check params - const username = options.username - const password = options.password - if (!username) { - console.error('username is required') - process.exit(1) - } - if (!password) { - console.error('password is required') - process.exit(1) - } - - await handleLoginCommand(options.remote, username, password) - - - }) - -program.parse(process.argv) diff --git a/packages/system-server/src/handler/function/index.ts b/packages/system-server/src/handler/function/index.ts index 0d5fbb63a2..35d2e40180 100644 --- a/packages/system-server/src/handler/function/index.ts +++ b/packages/system-server/src/handler/function/index.ts @@ -108,16 +108,16 @@ FunctionRouter.delete('/:func_id/triggers/:trigger_id', handleRemoveTrigger) /** * get function by name */ - FunctionRouter.get('/getFunction/:func_name', handleGetFunctionByName) + FunctionRouter.get('/detail/:func_name', handleGetFunctionByName) /** * update function by name */ - FunctionRouter.post('/updateFunction/:func_name', handleUpdateFunctionCodeByName) + FunctionRouter.post('/save/:func_name', handleUpdateFunctionCodeByName) /** * publish function by name */ -FunctionRouter.post('/publishFunction/:func_name', handlePublishOneFunctionByName) \ No newline at end of file +FunctionRouter.post('/publish/:func_name', handlePublishOneFunctionByName) \ No newline at end of file From f375241630e8b993c1d25ada82477e94226aac68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=81=E6=8C=AF=E6=8C=AF?= Date: Tue, 28 Jun 2022 17:30:09 +0800 Subject: [PATCH 12/12] add oss command --- packages/cli/src/actions/init.ts | 5 +- packages/cli/src/actions/oss.ts | 213 +++++++++++++++++++++++++++++++ packages/cli/src/api/oss.ts | 21 +++ packages/cli/src/api/request.ts | 14 +- packages/cli/src/api/user.ts | 2 +- packages/cli/src/index.ts | 15 +-- packages/cli/src/oss.ts | 48 +++++++ packages/cli/src/utils/tokens.ts | 2 +- packages/cli/src/utils/util.ts | 35 ++++- 9 files changed, 331 insertions(+), 24 deletions(-) create mode 100644 packages/cli/src/actions/oss.ts create mode 100644 packages/cli/src/api/oss.ts create mode 100644 packages/cli/src/oss.ts diff --git a/packages/cli/src/actions/init.ts b/packages/cli/src/actions/init.ts index 69686cbbef..9537fd366e 100644 --- a/packages/cli/src/actions/init.ts +++ b/packages/cli/src/actions/init.ts @@ -12,16 +12,17 @@ import { pipeline } from 'node:stream/promises' * @param {string} appName * @param {string} appid * @param {string} endPoint + * @param {string} ossEndpoint * @returns */ -export async function handleInitAppCommand(appName:string,appid:string,endPoint:string) { +export async function handleInitAppCommand(appName:string,appid:string,endPoint:string,ossEndpoint) { const appPath = path.resolve(process.cwd(), appName) checkDir(appPath) const lafFile = path.resolve(appPath, LAF_FILE) // write data - fs.writeFileSync(lafFile, JSON.stringify({ appid: appid, root: appPath ,endPoint})) + fs.writeFileSync(lafFile, JSON.stringify({ appid: appid, root: appPath ,endPoint,ossEndpoint})) } diff --git a/packages/cli/src/actions/oss.ts b/packages/cli/src/actions/oss.ts new file mode 100644 index 0000000000..df59860024 --- /dev/null +++ b/packages/cli/src/actions/oss.ts @@ -0,0 +1,213 @@ +import * as fs from 'node:fs' +import * as path from 'node:path' +import { createHash } from 'node:crypto' +import * as mime from 'mime' +import { checkDir, getS3Client } from '../utils/util' +import axios from 'axios' +import { pipeline } from 'node:stream/promises' + +/** + * push files + * @param {object} credentials + * @param {object} options + * @returns + */ +export async function handlePushCommand(credentials:any,options:any) { + const s3 = getS3Client(options.endpoint,credentials) + const source = options.source + + // get bucket objects + const res = await s3.listObjectsV2({ Bucket: options.bucketName, Delimiter: '' }).promise() + + // console.log(res.Contents) + const bucketObjects = res.Contents || [] + + // get source files + const abs_source = path.resolve(source) + checkDir(abs_source,false) + const sourceFiles = readdirRecursive(source).map(file => { + return { + key: path.relative(abs_source, file), + abs_path: path.resolve(file), + } + }) + + // get updated files + let updatedFiles = sourceFiles + if(!options.force) { + updatedFiles = getUpdatedFiles(sourceFiles, bucketObjects) + } + + console.log(`${updatedFiles.length} files need to be updated`) + + for (const file of updatedFiles) { + console.log(`uploading ${file.key} with: ${mime.getType(file.key)}`) + if (!options.dryRun) { + await s3.putObject({ + Bucket: options.bucketName, + Key: file.key, + Body: fs.readFileSync(path.resolve(source, file.abs_path)), + ContentType: mime.getType(file.key) + }).promise() + + console.log(path.resolve(source, file.abs_path)) + } + } + + // get deleted files // force 才delete + const deletedFiles = getDeletedFiles(sourceFiles, bucketObjects) + if(deletedFiles?.length > 0) { + console.log(`${deletedFiles.length} files need to be deleted`) + for (const obj of deletedFiles) { + console.log(`deleting ${obj.Key}`) + if (!options.dryRun) { + await s3.deleteObject({ Bucket: options.bucketName, Key: obj.Key }).promise() + } + } + } +} + + +/** + * pull files + * @param {object} credentials + * @param {object} options + * @returns + */ + +export async function handlePullCommand(credentials:any,options:any) { + + const s3 = getS3Client(options.endpoint,credentials) + + const outPath = options.outPath + + // get bucket objects + const res = await s3.listObjectsV2({ Bucket: options.bucketName, Delimiter: '' }).promise() + const bucketObjects = res.Contents || [] + + // get local files + const abs_source = path.resolve(outPath) + checkDir(abs_source) + const localFiles = readdirRecursive(outPath) + .map(file => { + return { + key: path.relative(abs_source, file), + abs_path: path.resolve(file), + } + }) + + let downFiles = bucketObjects + + + if(!options.force) { + downFiles = getDownFiles(localFiles, bucketObjects) + } + + + downFiles.forEach( async item => { + + const fileurl = s3.getSignedUrl('getObject', { Bucket: options.bucketName, Key: item.Key }) + const index = item.Key.lastIndexOf("/") + + if(index >0 ){ + const newDir = item.Key.substring(0,index) + const newPath = path.resolve(abs_source,newDir) + checkDir(newPath) + } + + const data = await axios({url: fileurl,method: 'GET',responseType: 'stream'}) + + const filepath = path.resolve(abs_source,item.Key) + + const writer = fs.createWriteStream(filepath) + + await pipeline(data.data,writer) + }) + + // get deleted files // force 才delete + const deletedFiles = getLocalDeletedFiles(localFiles, bucketObjects) + if(deletedFiles?.length > 0) { + console.log(`${deletedFiles.length} files need to be deleted`) + for (const obj of deletedFiles) { + console.log(`deleting ${obj.key}`) + fs.unlinkSync(obj.abs_path) + } + } + +} + + + +// readdir recursive +function readdirRecursive(dir: string) { + const files = fs.readdirSync(dir) + const result = [] + for (const file of files) { + const filepath = path.join(dir, file) + const stats = fs.statSync(filepath) + if (stats.isDirectory()) { + result.push(...readdirRecursive(filepath)) + } else { + result.push(filepath) + } + } + return result +} + +// get deleted files which are not in source files +function getDeletedFiles(sourceFiles: { key: string, abs_path: string }[], bucketObjects: any[]) { + const deletedFiles = bucketObjects.filter(bucketObject => { + const key = bucketObject.Key + return !sourceFiles.find(sourceFile => sourceFile.key === key) + }) + return deletedFiles +} + + +// get deleted files which are not in bucketObjects +function getLocalDeletedFiles(sourceFiles:any,bucketObjects:any){ + + const deletedFiles = sourceFiles.filter(sourceFile => { + const key = sourceFile.key + return !bucketObjects.find(bucketObject => bucketObject.Key === key) + }) + return deletedFiles + +} + +// get updated files +function getUpdatedFiles(sourceFiles: { key: string, abs_path: string }[], bucketObjects: any[]) { + const updatedFiles = sourceFiles.filter(sourceFile => { + const bucketObject = bucketObjects.find(bucketObject => bucketObject.Key === sourceFile.key) + if (!bucketObject) { + return true + } + return !compareFileMd5(sourceFile.abs_path, bucketObject) + }) + + return updatedFiles +} + +// get down files +function getDownFiles(sourceFiles:any,bucketObjects:any){ + + const downFiles = bucketObjects.filter(bucketObject => { + const sourceFile = sourceFiles.find(sourceFile => bucketObject.Key === sourceFile.key) + if (!sourceFile) { + return true + } + return !compareFileMd5(sourceFile.abs_path, bucketObject) + }) + + return downFiles + +} + +// compare file md5 +function compareFileMd5(sourceFile: string, bucketObject: any) { + + const source_data = fs.readFileSync(sourceFile) + const source_file_md5 = createHash('md5').update(source_data).digest('hex') + const etag = bucketObject.ETag.replace(/\"/g, '') + return source_file_md5 === etag +} \ No newline at end of file diff --git a/packages/cli/src/api/oss.ts b/packages/cli/src/api/oss.ts new file mode 100644 index 0000000000..779d07e53d --- /dev/null +++ b/packages/cli/src/api/oss.ts @@ -0,0 +1,21 @@ +import { requestData } from "./request" + + +/** + * 根据 appid 同步数据 + * @param {string} appid + * @param {string} bucketName + * @returns + */ + +export async function detail(appid: string,bucketName:string) { + + const url = `/sys-api/apps/${appid}/oss/buckets/${bucketName}` + const obj = { + method: "GET", + url, + } + + const result = await requestData(obj) + return result.data +} diff --git a/packages/cli/src/api/request.ts b/packages/cli/src/api/request.ts index 0ed49fb333..945160c084 100644 --- a/packages/cli/src/api/request.ts +++ b/packages/cli/src/api/request.ts @@ -51,23 +51,21 @@ request.interceptors.request.use( const status = error.response.status if (status === 401) { - console.error(error.response.data) - process.exit(1) } if (status === 403) { - console.error(error.response.data) - + process.exit(1) + } + if (status === 404) { + console.error(error.response.data) process.exit(1) } if (status === 422) { - - console.error(error.response.data) - - process.exit(1) + console.error(error.response.data) + process.exit(1) } // showError(error.message) diff --git a/packages/cli/src/api/user.ts b/packages/cli/src/api/user.ts index ad7414a48c..d51c51b595 100644 --- a/packages/cli/src/api/user.ts +++ b/packages/cli/src/api/user.ts @@ -21,7 +21,7 @@ export async function loginApi(server:string,obj:Object) { return false } - return {access_token:response.data.access_token,expire_time:response.data.expire} + return { access_token:response.data.access_token,expire_time:response.data.expire } }catch(err){ diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index fddff2322d..37f5ad22e4 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,19 +1,14 @@ #!/usr/bin/env node import { program } from 'commander' - import { handleLoginCommand } from './actions/user' - import { syncApp } from './api/sync' import { getApplicationByAppid } from './api/apps' import { handleInitAppCommand ,handleSyncAppCommand} from './actions/init' - import { appStop, appStart, appRestart } from './api/apps' import { handleAppListCommand } from './actions/app' - import { makeFnCommand} from './functions' - - +import { makeOssCommand} from './oss' program .option('-v, --version', 'output version') @@ -62,8 +57,9 @@ program const result = await getApplicationByAppid(appid) const appName = result.data.application.name const endPoint = `${result.data.app_deploy_url_schema}://${appid}.${result.data.app_deploy_host}` + const ossEndpoint = result.data.oss_external_endpoint - await handleInitAppCommand(appName,appid,endPoint) + await handleInitAppCommand(appName,appid,endPoint,ossEndpoint) // sync app data if (options.sync) { @@ -76,7 +72,6 @@ program catch (err) { console.log(err.message) } - }) @@ -115,7 +110,7 @@ program } else { console.log('start failed') } - }) + }) program @@ -134,6 +129,8 @@ program program.addCommand(makeFnCommand()) +program.addCommand(makeOssCommand()) + program.parse(process.argv) diff --git a/packages/cli/src/oss.ts b/packages/cli/src/oss.ts new file mode 100644 index 0000000000..c5aa70fb93 --- /dev/null +++ b/packages/cli/src/oss.ts @@ -0,0 +1,48 @@ +import { Command } from 'commander'; +import { detail } from './api/oss' +import { handlePushCommand ,handlePullCommand } from './actions/oss' + +import { getAppData } from "./utils/util" + +export function makeOssCommand() { + + const oss = new Command('oss') + + oss + .command('pull') + .argument('bucket',"bucket") + .argument('out-path',"out-path") + .option('-f, --force-overwrite', 'force to file ignore if modified', false) + .action(async (bucket,outPath,options) => { + + const appData = getAppData() + //get bucket detail + const buckets = await detail(appData.appid,bucket) + options.outPath= outPath + options.bucketName =`${appData.appid}-${bucket}` + options.endpoint =appData.ossEndpoint + await handlePullCommand( buckets.data.credentials,options) + + }); + + oss + .command('push') + .argument('input-path',"input-path") + .argument('bucket',"bucket") + .option('-f, --force-overwrite', 'force to file ignore if modified', false) + + .action(async(inputPath,bucket,options) => { + + const appData = getAppData() + // get bucket detail + const buckets = await detail(appData.appid,bucket) + options.source = inputPath + options.bucketName = `${appData.appid}-${bucket}` + options.endpoint = appData.ossEndpoint + await handlePushCommand( buckets.data.credentials,options) + + }); + + return oss; + +} diff --git a/packages/cli/src/utils/tokens.ts b/packages/cli/src/utils/tokens.ts index dab73086b9..4f40766635 100644 --- a/packages/cli/src/utils/tokens.ts +++ b/packages/cli/src/utils/tokens.ts @@ -16,7 +16,7 @@ export async function getAccessToken() { // check dir fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK | fs.constants.W_OK) } catch (err) { - console.error("please login first 2") + console.error("please login first ") process.exit(1) } diff --git a/packages/cli/src/utils/util.ts b/packages/cli/src/utils/util.ts index c6029f7794..b400029fd8 100644 --- a/packages/cli/src/utils/util.ts +++ b/packages/cli/src/utils/util.ts @@ -2,6 +2,7 @@ import * as fs from 'node:fs' import * as path from 'node:path' import { CREDENTIALS_DIR } from './constants' import { AUTH_FILE,LAF_FILE,FUNCTIONS_DIR } from '../utils/constants' +const AWS = require('aws-sdk') /** * check auth dir @@ -21,14 +22,22 @@ export function checkCredentialsDir(){ /** * check dir * @param {string} dir + * @param {boolean} flag */ -export function checkDir(dir:string){ +export function checkDir(dir:string,flag:boolean=true){ try{ // check dir fs.accessSync(dir, fs.constants.R_OK|fs.constants.W_OK) }catch(err){ // mkdir - fs.mkdirSync(dir, { recursive: true }) + if(flag){ + fs.mkdirSync(dir, { recursive: true }) + }else{ + + console.error('dir not exist') + process.exit(1) + } + } } @@ -88,7 +97,7 @@ export function getRemoteServe(){ // check dir fs.accessSync(CREDENTIALS_DIR, fs.constants.R_OK|fs.constants.W_OK) }catch(err){ - console.error("please login first 3") + console.error("please login first") process.exit(1) } const authData = JSON.parse(fs.readFileSync(AUTH_FILE, 'utf8')); @@ -116,4 +125,24 @@ export function getAppData(){ } +/** + * get s3 client + * @param endpoint + * @param credentials + * @returns + */ + export function getS3Client(endpoint: string, credentials:any) { + + return new AWS.S3({ + accessKeyId: credentials.accessKeyId, + secretAccessKey: credentials.secretAccessKey, + sessionToken: credentials.sessionToken, + endpoint: endpoint, + s3ForcePathStyle: true, + signatureVersion: 'v4', + region: 'us-east-1' + }) + + } + \ No newline at end of file