UNPKG

6.3 kBPlain TextView Raw
1import path from 'path'
2import CloudBase from '@cloudbase/manager-node'
3import { StorageService } from '@cloudbase/manager-node/types/storage'
4import {
5 CloudApiService,
6 firstLetterToLowerCase,
7 isDirectory,
8 checkAndGetCredential,
9 getProxy,
10 genClickableLink,
11 checkReadable
12} from './utils'
13import { CloudBaseError } from './error'
14
15async function getStorageService(envId: string): Promise<StorageService> {
16 const { secretId, secretKey, token } = await checkAndGetCredential(true)
17 const app = new CloudBase({
18 secretId,
19 secretKey,
20 token,
21 envId,
22 proxy: getProxy()
23 })
24 return app.storage
25}
26
27interface IBaseOptions {
28 envId: string
29}
30
31interface IHostingFileOptions extends IBaseOptions {
32 filePath: string
33 cloudPath: string
34 isDir: boolean
35 onProgress?: (data: any) => void
36 onFileFinish?: (...args) => void
37}
38
39interface IHostingCloudOptions extends IBaseOptions {
40 cloudPath: string
41 isDir: boolean
42}
43
44const HostingStatusMap = {
45 init: '初始化中',
46 process: '处理中',
47 online: '上线',
48 destroying: '销毁中',
49 offline: '下线',
50 create_fail: '初始化失败', // eslint-disable-line
51 destroy_fail: '销毁失败' // eslint-disable-line
52}
53
54const tcbService = CloudApiService.getInstance('tcb')
55
56export async function getHostingInfo(options: IBaseOptions) {
57 const { envId } = options
58 const res = await tcbService.request('DescribeStaticStore', {
59 EnvId: envId
60 })
61 const data = firstLetterToLowerCase(res)
62 return data
63}
64
65async function checkHostingStatus(envId: string) {
66 const hostings = await getHostingInfo({ envId })
67
68 const link = genClickableLink('https://console.cloud.tencent.com/tcb')
69
70 if (!hostings.data || !hostings.data.length) {
71 throw new CloudBaseError(
72 `您还没有开启静态网站服务,请先到云开发控制台开启静态网站服务!\n👉 ${link}`,
73 {
74 code: 'INVALID_OPERATION'
75 }
76 )
77 }
78
79 const website = hostings.data[0]
80
81 if (website.status !== 'online') {
82 throw new CloudBaseError(
83 `静态网站服务【${HostingStatusMap[website.status]}】,无法进行此操作!`,
84 {
85 code: 'INVALID_OPERATION'
86 }
87 )
88 }
89
90 return website
91}
92
93export async function enableHosting(options: IBaseOptions) {
94 const { envId } = options
95 const hostings = await getHostingInfo(options)
96 if (hostings.data && hostings.data.length) {
97 const website = hostings.data[0]
98 // offline 状态的服务可重新开启
99 if (website.status !== 'offline') {
100 throw new CloudBaseError('静态网站服务已开启,请勿重复操作!')
101 }
102 }
103
104 const res = await tcbService.request('CreateStaticStore', {
105 EnvId: envId
106 })
107 const code = res.Result === 'succ' ? 0 : -1
108 return {
109 code,
110 requestId: res.RequestId
111 }
112}
113
114// 展示文件信息
115export async function hostingList(options: IBaseOptions) {
116 const { envId } = options
117 const hosting = await checkHostingStatus(envId)
118 const { bucket, regoin } = hosting
119 const storageService = await getStorageService(envId)
120
121 const list = await storageService.walkCloudDirCustom({
122 prefix: '',
123 bucket,
124 region: regoin
125 })
126
127 return list
128}
129
130export async function destroyHosting(options: IBaseOptions) {
131 const { envId } = options
132 const files = await hostingList(options)
133
134 if (files?.length) {
135 throw new CloudBaseError('静态网站文件不为空,无法销毁!', {
136 code: 'INVALID_OPERATION'
137 })
138 }
139
140 const hostings = await getHostingInfo(options)
141
142 if (!hostings.data || !hostings.data.length) {
143 throw new CloudBaseError('静态网站服务未开启!', {
144 code: 'INVALID_OPERATION'
145 })
146 }
147
148 const website = hostings.data[0]
149
150 // destroy_fail 状态可重试
151 if (website.status !== 'online' && website.status !== 'destroy_fail') {
152 throw new CloudBaseError(
153 `静态网站服务【${HostingStatusMap[website.status]}】,无法进行此操作!`,
154 {
155 code: 'INVALID_OPERATION'
156 }
157 )
158 }
159
160 const res = await tcbService.request('DestroyStaticStore', {
161 EnvId: envId
162 })
163
164 const code = res.Result === 'succ' ? 0 : -1
165 return {
166 code,
167 requestId: res.RequestId
168 }
169}
170
171// 上传文件
172export async function hostingDeploy(options: IHostingFileOptions) {
173 const { envId, filePath, cloudPath, onProgress, onFileFinish } = options
174 const resolvePath = path.resolve(filePath)
175 // 检查路径是否存在
176 checkReadable(resolvePath, true)
177
178 const hosting = await checkHostingStatus(envId)
179 const { bucket, regoin } = hosting
180 const storageService = await getStorageService(envId)
181
182 if (isDirectory(resolvePath)) {
183 await storageService.uploadDirectoryCustom({
184 localPath: resolvePath,
185 cloudPath,
186 bucket,
187 region: regoin,
188 onProgress,
189 onFileFinish,
190 fileId: false
191 })
192 } else {
193 const assignCloudPath = cloudPath || path.parse(resolvePath).base
194 await storageService.uploadFileCustom({
195 localPath: resolvePath,
196 cloudPath: assignCloudPath,
197 bucket,
198 region: regoin,
199 onProgress,
200 fileId: false
201 })
202 }
203}
204
205// 删除文件
206export async function hostingDelete(options: IHostingCloudOptions) {
207 const { envId, cloudPath, isDir } = options
208 const hosting = await checkHostingStatus(envId)
209 const { bucket, regoin } = hosting
210 const storageService = await getStorageService(envId)
211
212 if (isDir) {
213 await storageService.deleteDirectoryCustom({
214 cloudPath,
215 bucket,
216 region: regoin
217 })
218 } else {
219 await storageService.deleteFileCustom([cloudPath], bucket, regoin)
220 }
221}
222
223// 删除文件
224export async function walkLocalDir(envId: string, dir: string) {
225 const storageService = await getStorageService(envId)
226 return storageService.walkLocalDir(dir)
227}