1 | import path from 'path'
|
2 | import CloudBase from '@cloudbase/manager-node'
|
3 | import { StorageService } from '@cloudbase/manager-node/types/storage'
|
4 | import {
|
5 | CloudApiService,
|
6 | firstLetterToLowerCase,
|
7 | isDirectory,
|
8 | checkAndGetCredential,
|
9 | getProxy,
|
10 | genClickableLink,
|
11 | checkReadable
|
12 | } from './utils'
|
13 | import { CloudBaseError } from './error'
|
14 |
|
15 | async 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 |
|
27 | interface IBaseOptions {
|
28 | envId: string
|
29 | }
|
30 |
|
31 | interface IHostingFileOptions extends IBaseOptions {
|
32 | filePath: string
|
33 | cloudPath: string
|
34 | isDir: boolean
|
35 | onProgress?: (data: any) => void
|
36 | onFileFinish?: (...args) => void
|
37 | }
|
38 |
|
39 | interface IHostingCloudOptions extends IBaseOptions {
|
40 | cloudPath: string
|
41 | isDir: boolean
|
42 | }
|
43 |
|
44 | const HostingStatusMap = {
|
45 | init: '初始化中',
|
46 | process: '处理中',
|
47 | online: '上线',
|
48 | destroying: '销毁中',
|
49 | offline: '下线',
|
50 | create_fail: '初始化失败',
|
51 | destroy_fail: '销毁失败'
|
52 | }
|
53 |
|
54 | const tcbService = CloudApiService.getInstance('tcb')
|
55 |
|
56 | export 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 |
|
65 | async 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 |
|
93 | export 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 |
|
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 |
|
115 | export 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 |
|
130 | export 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 |
|
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 |
|
172 | export 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 |
|
206 | export 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 |
|
224 | export async function walkLocalDir(envId: string, dir: string) {
|
225 | const storageService = await getStorageService(envId)
|
226 | return storageService.walkLocalDir(dir)
|
227 | }
|