xiongzhu 2 лет назад
Родитель
Сommit
2c15905696
47 измененных файлов с 1134 добавлено и 118 удалено
  1. 4 2
      .adonisrc.json
  2. 4 0
      .env.example
  3. 46 0
      app/Controllers/Http/AuthController.ts
  4. 44 0
      app/Controllers/Http/CategoriesController.ts
  5. 70 0
      app/Controllers/Http/CollectionsController.ts
  6. 2 0
      app/Controllers/Http/EpisodesController.ts
  7. 44 0
      app/Controllers/Http/FilesController.ts
  8. 25 10
      app/Controllers/Http/SeriesController.ts
  9. 0 44
      app/Controllers/Http/TrackingsController.ts
  10. 5 0
      app/Controllers/Http/UsersController.ts
  11. 5 0
      app/Models/AppBaseModel.ts
  12. 3 2
      app/Models/BalanceRecord.ts
  13. 16 0
      app/Models/Category.ts
  14. 3 2
      app/Models/Collection.ts
  15. 7 3
      app/Models/Episode.ts
  16. 3 2
      app/Models/Order.ts
  17. 16 2
      app/Models/Series.ts
  18. 11 2
      app/Models/User.ts
  19. 3 2
      app/Models/UserBalance.ts
  20. 30 8
      app/Services/PaginationService.ts
  21. 5 1
      app/Services/UserBalanceService.ts
  22. 7 0
      app/Strategies/CamelCaseNamingStrategy.ts
  23. 45 0
      config/ally.ts
  24. 1 1
      config/database.ts
  25. 1 1
      config/drive.ts
  26. 19 0
      contracts/ally.ts
  27. 4 3
      database/migrations/1700557040664_users.ts
  28. 4 3
      database/migrations/1700727015979_series.ts
  29. 2 2
      database/migrations/1700736183031_episodes.ts
  30. 3 3
      database/migrations/1700738247787_collections.ts
  31. 2 2
      database/migrations/1700739168060_balance_records.ts
  32. 2 2
      database/migrations/1700739735030_orders.ts
  33. 2 2
      database/migrations/1700812429489_user_balances.ts
  34. 22 0
      database/migrations/1701165136391_categories.ts
  35. 20 0
      database/migrations/1701165722492_category_series.ts
  36. 22 0
      database/seeders/Category.ts
  37. 20 0
      database/seeders/File.ts
  38. 7 1
      database/seeders/Series.ts
  39. 3 2
      database/seeders/User.ts
  40. 5 1
      env.ts
  41. 8 0
      package.json
  42. 4 0
      providers/AppProvider.ts
  43. 6 1
      start/bouncer.ts
  44. 21 0
      start/events.ts
  45. 28 4
      start/routes.ts
  46. 2 1
      tsconfig.json
  47. 528 9
      yarn.lock

+ 4 - 2
.adonisrc.json

@@ -17,7 +17,8 @@
   "preloads": [
     "./start/routes",
     "./start/kernel",
-    "./start/bouncer"
+    "./start/bouncer",
+    "./start/events"
   ],
   "providers": [
     "./providers/AppProvider",
@@ -25,7 +26,8 @@
     "@adonisjs/lucid",
     "@adonisjs/auth",
     "@adonisjs/bouncer",
-    "@adonisjs/drive-s3"
+    "@adonisjs/drive-s3",
+    "@adonisjs/ally"
   ],
   "aceProviders": [
     "@adonisjs/repl"

+ 4 - 0
.env.example

@@ -9,3 +9,7 @@ MYSQL_PORT=3306
 MYSQL_USER=lucid
 MYSQL_PASSWORD=
 MYSQL_DB_NAME=lucid
+GOOGLE_CLIENT_ID=clientId
+GOOGLE_CLIENT_SECRET=clientSecret
+FACEBOOK_CLIENT_ID=clientId
+FACEBOOK_CLIENT_SECRET=clientSecret

+ 46 - 0
app/Controllers/Http/AuthController.ts

@@ -1,4 +1,7 @@
+import { AuthenticationException } from '@adonisjs/auth/build/standalone'
 import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
+import User, { UserRoles } from 'App/Models/User'
+import { schema, rules } from '@ioc:Adonis/Core/Validator'
 
 export default class AuthController {
     public async login({ request, auth }: HttpContextContract) {
@@ -7,4 +10,47 @@ export default class AuthController {
             expiresIn: '30 days'
         })
     }
+
+    public async loginAdmin({ request, auth }: HttpContextContract) {
+        const { username, password } = request.all()
+        let token
+        try {
+            token = await auth.use('api').attempt(username, password, {
+                expiresIn: '30 days'
+            })
+        } catch (error) {
+            throw new AuthenticationException(error.message, error.code)
+        }
+        if (token.user.role !== UserRoles.Admin) {
+            throw new AuthenticationException('Unauthorized access', 'E_UNAUTHORIZED_ACCESS')
+        }
+        return token
+    }
+
+    public async register({ request, auth }: HttpContextContract) {
+        const data = await request.validate({
+            schema: schema.create({
+                username: schema.string({ trim: true }, [
+                    rules.regex(/^[a-zA-Z0-9_]{4,16}$/),
+                    rules.unique({ table: 'users', column: 'username' })
+                ]),
+                email: schema.string({ trim: true }, [
+                    rules.email(),
+                    rules.unique({ table: 'users', column: 'email' })
+                ]),
+                password: schema.string({ trim: true }, [rules.minLength(6), rules.maxLength(18)])
+            }),
+            messages: {
+                minLength: '{{field}} 最小长度为 {{ options.minLength }}',
+                maxLength: '{{field}} 最大长度为 {{ options.maxLength }}',
+                unique: '{{field}} 不可用',
+                email: '{{field}} 不是有效的邮箱',
+                regex: '{{field}} 只能包含字母、数字和下划线,长度为4-16位'
+            }
+        })
+        const user = new User()
+        user.merge(data)
+        await user.save()
+        return await auth.use('api').login(user)
+    }
 }

+ 44 - 0
app/Controllers/Http/CategoriesController.ts

@@ -0,0 +1,44 @@
+import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
+import Category from 'App/Models/Category'
+import PaginationService from 'App/Services/PaginationService'
+import { schema } from '@ioc:Adonis/Core/Validator'
+
+export default class CategoriesController {
+    private paginationService = new PaginationService(Category)
+
+    public async index({ request }: HttpContextContract) {
+        return await this.paginationService.paginate(request.all())
+    }
+
+    public async store({ request, bouncer }: HttpContextContract) {
+        await bouncer.authorize('admin')
+        const data = await request.validate({
+            schema: schema.create({
+                name: schema.string()
+            })
+        })
+        return await Category.create(data)
+    }
+
+    public async show({ params }: HttpContextContract) {
+        return await Category.findOrFail(params.id)
+    }
+
+    public async update({ params, request, bouncer }: HttpContextContract) {
+        await bouncer.authorize('admin')
+        const category = await Category.findOrFail(params.id)
+        const data = await request.validate({
+            schema: schema.create({
+                name: schema.string()
+            })
+        })
+        category.merge(data)
+        return await category.save()
+    }
+
+    public async destroy({ params, bouncer }: HttpContextContract) {
+        await bouncer.authorize('admin')
+        const category = await Category.findOrFail(params.id)
+        await category.delete()
+    }
+}

+ 70 - 0
app/Controllers/Http/CollectionsController.ts

@@ -0,0 +1,70 @@
+import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
+import Collection from 'App/Models/Collection'
+import PaginationService from 'App/Services/PaginationService'
+import { schema } from '@ioc:Adonis/Core/Validator'
+import Episode from 'App/Models/Episode'
+
+export default class CollectionsController {
+    private paginationService = new PaginationService(Collection)
+    public async index({ request, auth }: HttpContextContract) {
+        return await this.paginationService.paginate({
+            ...request.all(),
+            userId: auth.user!.id
+        })
+    }
+
+    public async store({ request, auth }: HttpContextContract) {
+        const data = await request.validate({
+            schema: schema.create({
+                seriesId: schema.number(),
+                curEpId: schema.number()
+            })
+        })
+        let collection = await Collection.query()
+            .where('user_id', auth.user!.id)
+            .where('series_id', data.seriesId)
+            .first()
+        const ep = await Episode.findOrFail(data.curEpId)
+        if (collection) {
+            collection.curEpId = data.curEpId
+            collection.curEpNum = ep.episodeNum
+            await collection.save()
+        } else {
+            collection = await Collection.create({
+                userId: auth.user!.id,
+                seriesId: data.seriesId,
+                curEpId: data.curEpId,
+                curEpNum: ep.episodeNum
+            })
+        }
+        return collection
+    }
+
+    public async show({ params, auth }: HttpContextContract) {
+        return await Collection.query()
+            .where('userId', auth.user!.id)
+            .where('seriesId', params.id)
+            .firstOrFail()
+    }
+
+    public async update({ request, params, bouncer }: HttpContextContract) {
+        const data = await request.validate({
+            schema: schema.create({
+                userId: schema.number.optional(),
+                seriesId: schema.number.optional(),
+                curEpId: schema.number.optional()
+            })
+        })
+        const collection = await Collection.findOrFail(params.id)
+        await bouncer.authorize('owner', collection)
+        collection.merge(data)
+        return await collection.save()
+    }
+
+    public async destroy({ params, auth }: HttpContextContract) {
+        await Collection.query()
+            .where('userId', auth.user!.id)
+            .where('seriesId', params.id)
+            .delete()
+    }
+}

+ 2 - 0
app/Controllers/Http/EpisodesController.ts

@@ -4,6 +4,7 @@ import PaginationService from 'App/Services/PaginationService'
 import { schema } from '@ioc:Adonis/Core/Validator'
 import Decimal from 'decimal.js'
 import Order from 'App/Models/Order'
+import Drive from '@ioc:Adonis/Core/Drive'
 
 export default class EpisodesController {
     private paginationService = new PaginationService(Episode)
@@ -49,6 +50,7 @@ export default class EpisodesController {
             }
             if (order) {
                 episode.purchased = true
+                episode.signedUrl = await Drive.getSignedUrl(episode.video!)
             }
         }
         return episode

+ 44 - 0
app/Controllers/Http/FilesController.ts

@@ -0,0 +1,44 @@
+import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
+import Drive from '@ioc:Adonis/Core/Drive'
+import STSClient, { AssumeRoleRequest } from '@alicloud/sts20150401'
+import { Config } from '@alicloud/openapi-client'
+import { RuntimeOptions } from '@alicloud/tea-util'
+export default class FilesController {
+    public async store({ request }: HttpContextContract) {
+        const file = request.file('file')
+        await file?.moveToDisk('./uploads', { visibility: 'public' })
+        // const signedUrl = await Drive.getSignedUrl(file?.fileName!)
+        return { url: file?.filePath }
+    }
+
+    public async sts() {
+        // const client = new STSClient({
+        //     credentials: {
+        //         accessKeyId: 'LTAI5tQYh51ihAYExgrrs7Fu',
+        //         secretAccessKey: 'fpEdyOcPWsDSEwCjKkZgnEC8ZKnmAk'
+        //     },
+        //     region: 'cn-hangzhou',
+        //     endpoint: 'https://sts.cn-hangzhou.aliyuncs.com'
+        //     // apiVersion: '2015-04-01'
+        // })
+        const client = new STSClient(
+            new Config({
+                accessKeyId: 'LTAI5tQYh51ihAYExgrrs7Fu',
+                accessKeySecret: 'fpEdyOcPWsDSEwCjKkZgnEC8ZKnmAk',
+                endpoint: 'sts.cn-hangzhou.aliyuncs.com'
+            })
+        )
+        let assumeRoleRequest = new AssumeRoleRequest({
+            durationSeconds: 1000,
+            roleArn: 'acs:ram::1037005998695187:role/oss-read',
+            roleSessionName: 'user'
+        })
+        try {
+            // 复制代码运行请自行打印 API 的返回值
+            return await client.assumeRoleWithOptions(assumeRoleRequest, new RuntimeOptions())
+        } catch (error) {
+            // 错误 message
+            console.log(error)
+        }
+    }
+}

+ 25 - 10
app/Controllers/Http/SeriesController.ts

@@ -1,12 +1,15 @@
 import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
-import Serie from 'App/Models/Series'
+import Series from 'App/Models/Series'
 import PaginationService from 'App/Services/PaginationService'
 import { schema } from '@ioc:Adonis/Core/Validator'
+import Decimal from 'decimal.js'
 
 export default class SeriesController {
-    private paginationService = new PaginationService(Serie)
+    private paginationService = new PaginationService(Series)
     public async index({ request }: HttpContextContract) {
-        return await this.paginationService.paginate(request.all())
+        return await this.paginationService.paginate(request.all(), (q) => {
+            q.preload('categories')
+        })
     }
 
     public async store({ request }: HttpContextContract) {
@@ -20,31 +23,43 @@ export default class SeriesController {
                 meta: schema.object.optional().anyMembers()
             })
         })
-        return await Serie.create(request.all())
+        return await Series.create(request.all())
     }
 
     public async show({ params }: HttpContextContract) {
-        return await Serie.findOrFail(params.id)
+        const data = await Series.findOrFail(params.id)
+        await data.load('categories')
+        return data
     }
 
     public async update({ params, request }: HttpContextContract) {
-        const serie = await Serie.findOrFail(params.id)
-        const payload = await request.validate({
+        const serie = await Series.findOrFail(params.id)
+        const payload = (await request.validate({
             schema: schema.create({
                 title: schema.string.optional(),
                 description: schema.string.optional(),
                 cover: schema.string.optional(),
                 tags: schema.array.optional().members(schema.string()),
                 releaseDate: schema.date.optional(),
-                meta: schema.object.optional().anyMembers()
+                meta: schema.object.optional().anyMembers(),
+                price: schema.string.optional(),
+                categories: schema.array.optional().anyMembers(),
+                totalEpisodes: schema.number.optional()
             })
-        })
+        })) as any
+        if (payload.price) {
+            payload.price = new Decimal(payload.price)
+        }
+        if (payload.categories) {
+            await serie.related('categories').sync(payload.categories.map((item: any) => item.id))
+            delete payload.categories
+        }
         serie.merge(payload)
         return await serie.save()
     }
 
     public async destroy({ params }: HttpContextContract) {
-        const serie = await Serie.findOrFail(params.id)
+        const serie = await Series.findOrFail(params.id)
         await serie.delete()
     }
 }

+ 0 - 44
app/Controllers/Http/TrackingsController.ts

@@ -1,44 +0,0 @@
-import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
-import Tracking from 'App/Models/Tracking'
-import PaginationService from 'App/Services/PaginationService'
-import { schema } from '@ioc:Adonis/Core/Validator'
-
-export default class TrackingsController {
-    private paginationService = new PaginationService(Tracking)
-    public async index({ request }: HttpContextContract) {
-        return await this.paginationService.paginate(request.all())
-    }
-
-    public async store({ request }: HttpContextContract) {
-        const data = await request.validate({
-            schema: schema.create({
-                userId: schema.number(),
-                seriesId: schema.number(),
-                curEpId: schema.number()
-            })
-        })
-        return await Tracking.create(data)
-    }
-
-    public async show({ params }: HttpContextContract) {
-        return await Tracking.findOrFail(params.id)
-    }
-
-    public async update({ request, params }: HttpContextContract) {
-        const data = await request.validate({
-            schema: schema.create({
-                userId: schema.number.optional(),
-                seriesId: schema.number.optional(),
-                curEpId: schema.number.optional()
-            })
-        })
-        const tracking = await Tracking.findOrFail(params.id)
-        tracking.merge(data)
-        return await tracking.save()
-    }
-
-    public async destroy({ params }: HttpContextContract) {
-        const tracking = await Tracking.findOrFail(params.id)
-        await tracking.delete()
-    }
-}

+ 5 - 0
app/Controllers/Http/UsersController.ts

@@ -42,4 +42,9 @@ export default class UsersController {
     public async my({ auth }: HttpContextContract) {
         return auth.user
     }
+
+    public async myAdmin({ auth, bouncer }: HttpContextContract) {
+        await bouncer.authorize('admin')
+        return auth.user
+    }
 }

+ 5 - 0
app/Models/AppBaseModel.ts

@@ -0,0 +1,5 @@
+import { BaseModel } from '@ioc:Adonis/Lucid/Orm'
+import CamelCaseNamingStrategy from 'App/Strategies/CamelCaseNamingStrategy'
+export default class AppBaseModel extends BaseModel {
+    public static namingStrategy = new CamelCaseNamingStrategy()
+}

+ 3 - 2
app/Models/BalanceRecord.ts

@@ -1,9 +1,10 @@
 import { DateTime } from 'luxon'
-import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
+import { column } from '@ioc:Adonis/Lucid/Orm'
 import Decimal from 'decimal.js'
 import { decimalConverter } from 'App/Helpers/db'
+import AppBaseModel from './AppBaseModel'
 
-export default class BalanceRecord extends BaseModel {
+export default class BalanceRecord extends AppBaseModel {
     @column({ isPrimary: true })
     public id: number
 

+ 16 - 0
app/Models/Category.ts

@@ -0,0 +1,16 @@
+import { DateTime } from 'luxon'
+import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
+
+export default class Category extends BaseModel {
+    @column({ isPrimary: true })
+    public id: number
+
+    @column.dateTime({ autoCreate: true })
+    public createdAt: DateTime
+
+    @column.dateTime({ autoCreate: true, autoUpdate: true })
+    public updatedAt: DateTime
+
+    @column()
+    public name: string
+}

+ 3 - 2
app/Models/Tracking.ts → app/Models/Collection.ts

@@ -1,7 +1,8 @@
 import { DateTime } from 'luxon'
-import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
+import { column } from '@ioc:Adonis/Lucid/Orm'
+import AppBaseModel from './AppBaseModel'
 
-export default class Tracking extends BaseModel {
+export default class Collection extends AppBaseModel {
     @column({ isPrimary: true })
     public id: number
 

+ 7 - 3
app/Models/Episode.ts

@@ -1,9 +1,11 @@
 import { DateTime } from 'luxon'
-import { BaseModel, column, computed } from '@ioc:Adonis/Lucid/Orm'
+import { column, computed } from '@ioc:Adonis/Lucid/Orm'
 import Decimal from 'decimal.js'
 import { decimalConverter } from 'App/Helpers/db'
+import AppBaseModel from './AppBaseModel'
+export default class Episode extends AppBaseModel {
+    // public static namingStrategy = new CamelCaseNamingStrategy()
 
-export default class Episode extends BaseModel {
     @column({ isPrimary: true })
     public id: number
 
@@ -39,10 +41,12 @@ export default class Episode extends BaseModel {
 
     public purchased: boolean = false
 
+    public signedUrl: string
+
     @computed()
     public get playUrl() {
         if (this.purchased) {
-            return this.video
+            return this.signedUrl
         }
         return null
     }

+ 3 - 2
app/Models/Order.ts

@@ -1,9 +1,10 @@
 import { DateTime } from 'luxon'
-import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
+import { column } from '@ioc:Adonis/Lucid/Orm'
 import { decimalConverter } from 'App/Helpers/db'
 import Decimal from 'decimal.js'
+import AppBaseModel from './AppBaseModel'
 
-export default class Order extends BaseModel {
+export default class Order extends AppBaseModel {
     @column({ isPrimary: true })
     public id: number
 

+ 16 - 2
app/Models/Series.ts

@@ -1,8 +1,10 @@
 import { DateTime } from 'luxon'
-import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
+import { ManyToMany, column, manyToMany } from '@ioc:Adonis/Lucid/Orm'
 import { stringArrayConverter, jsonConverter, decimalConverter } from 'App/Helpers/db'
 import Decimal from 'decimal.js'
-export default class Serie extends BaseModel {
+import AppBaseModel from './AppBaseModel'
+import Category from './Category'
+export default class Serie extends AppBaseModel {
     @column({ isPrimary: true })
     public id: number
 
@@ -24,6 +26,15 @@ export default class Serie extends BaseModel {
     @column({ ...stringArrayConverter })
     public tags?: string[]
 
+    @manyToMany(() => Category, {
+        pivotTable: 'category_series',
+        localKey: 'id',
+        pivotForeignKey: 'series_id',
+        relatedKey: 'id',
+        pivotRelatedForeignKey: 'category_id'
+    })
+    public categories: ManyToMany<typeof Category>
+
     @column.dateTime()
     public releaseDate?: DateTime
 
@@ -35,4 +46,7 @@ export default class Serie extends BaseModel {
 
     @column({ ...decimalConverter })
     public price: Decimal
+
+    @column()
+    public totalEpisodes: number
 }

+ 11 - 2
app/Models/User.ts

@@ -1,8 +1,14 @@
 import { DateTime } from 'luxon'
-import { BaseModel, beforeSave, column } from '@ioc:Adonis/Lucid/Orm'
+import { beforeSave, column } from '@ioc:Adonis/Lucid/Orm'
 import Hash from '@ioc:Adonis/Core/Hash'
+import AppBaseModel from './AppBaseModel'
 
-export default class User extends BaseModel {
+export enum UserRoles {
+    Admin = 'admin',
+    User = 'user'
+}
+
+export default class User extends AppBaseModel {
     @beforeSave()
     public static async hashPassword(user: User) {
         if (user.$dirty.password) {
@@ -19,6 +25,9 @@ export default class User extends BaseModel {
     @column.dateTime({ autoCreate: true, autoUpdate: true })
     public updatedAt: DateTime
 
+    @column()
+    public role: UserRoles = UserRoles.User
+
     @column()
     public username: string
 

+ 3 - 2
app/Models/UserBalance.ts

@@ -1,9 +1,10 @@
 import { DateTime } from 'luxon'
-import { BaseModel, column } from '@ioc:Adonis/Lucid/Orm'
+import { column } from '@ioc:Adonis/Lucid/Orm'
 import Decimal from 'decimal.js'
 import { decimalConverter } from 'App/Helpers/db'
+import AppBaseModel from './AppBaseModel'
 
-export default class UserBalance extends BaseModel {
+export default class UserBalance extends AppBaseModel {
     @column({ isPrimary: true })
     public id: number
 

+ 30 - 8
app/Services/PaginationService.ts

@@ -1,14 +1,25 @@
-import { LucidModel } from '@ioc:Adonis/Lucid/Orm'
+import { LucidModel, ModelQueryBuilderContract } from '@ioc:Adonis/Lucid/Orm'
 
-export default class PaginationService {
-    protected model: LucidModel
-    constructor(model: LucidModel) {
+export default class PaginationService<T extends LucidModel> {
+    protected model: T
+    constructor(model: T) {
         this.model = model
     }
-    public async paginate(query: any) {
+    public async paginate(
+        query: any,
+        injectQuery?: (q: ModelQueryBuilderContract<T, InstanceType<T>>) => void
+    ) {
         let q = this.model.query()
+        if (injectQuery) {
+            injectQuery(q)
+        }
         Object.keys(query).forEach((key) => {
-            if (key === 'page' || key === 'pageSize') {
+            if (
+                ['page', 'pageSize', 'orderBy', 'preload'].includes(key) ||
+                query[key] === undefined ||
+                query[key] === null ||
+                query[key] === ''
+            ) {
                 return
             }
             if (this.model.$hasColumn(key)) {
@@ -16,8 +27,8 @@ export default class PaginationService {
             }
         })
         let orders: { column: string; order: 'asc' | 'desc' }[] = []
-        if (query.orderBy) {
-            query.orderBy.split(';').forEach((e) => {
+        if (query.order) {
+            query.order.split(';').forEach((e) => {
                 let [column, order] = e.split(',')
                 if (this.model.$hasColumn(column)) {
                     orders.push({ column, order: order || 'asc' })
@@ -28,6 +39,17 @@ export default class PaginationService {
             orders.push({ column: 'createdAt', order: 'desc' })
         }
         q.orderBy(orders)
+        if (query.preload) {
+            if (query.preload === 'true' || query.preload === true) {
+                this.model.$relationsDefinitions.forEach((e) => {
+                    q.preload(e.relationName as any)
+                })
+            } else {
+                query.preload.split(',').forEach((e) => {
+                    q.preload(e)
+                })
+            }
+        }
         return await q.paginate(query.page || 1, query.pageSize || 20)
     }
 }

+ 5 - 1
app/Services/UserBalanceService.ts

@@ -6,7 +6,11 @@ class UserBalanceService {
     public async getBalance(userId: number) {
         let userBalance = await UserBalance.findBy('userId', userId)
         if (!userBalance) {
-            userBalance = await UserBalance.create({ userId })
+            userBalance = await UserBalance.create({
+                userId,
+                balance: new Decimal(0),
+                lastBalance: new Decimal(0)
+            })
         }
         return userBalance
     }

+ 7 - 0
app/Strategies/CamelCaseNamingStrategy.ts

@@ -0,0 +1,7 @@
+import { BaseModel, SnakeCaseNamingStrategy } from '@ioc:Adonis/Lucid/Orm'
+import { string } from '@ioc:Adonis/Core/Helpers'
+export default class CamelCaseNamingStrategy extends SnakeCaseNamingStrategy {
+    public serializedName(_model: typeof BaseModel, propertyName: string) {
+        return string.camelCase(propertyName)
+    }
+}

+ 45 - 0
config/ally.ts

@@ -0,0 +1,45 @@
+/**
+ * Config source: https://git.io/JOdi5
+ *
+ * Feel free to let us know via PR, if you find something broken in this config
+ * file.
+ */
+
+import Env from '@ioc:Adonis/Core/Env'
+import { AllyConfig } from '@ioc:Adonis/Addons/Ally'
+
+/*
+|--------------------------------------------------------------------------
+| Ally Config
+|--------------------------------------------------------------------------
+|
+| The `AllyConfig` relies on the `SocialProviders` interface which is
+| defined inside `contracts/ally.ts` file.
+|
+*/
+const allyConfig: AllyConfig = {
+    /*
+	|--------------------------------------------------------------------------
+	| Google driver
+	|--------------------------------------------------------------------------
+	*/
+    google: {
+        driver: 'google',
+        clientId: Env.get('GOOGLE_CLIENT_ID'),
+        clientSecret: Env.get('GOOGLE_CLIENT_SECRET'),
+        callbackUrl: 'http://localhost:3333/google/callback'
+    },
+    /*
+    |--------------------------------------------------------------------------
+    | Facebook driver
+    |--------------------------------------------------------------------------
+    */
+    facebook: {
+        driver: 'facebook',
+        clientId: Env.get('FACEBOOK_CLIENT_ID'),
+        clientSecret: Env.get('FACEBOOK_CLIENT_SECRET'),
+        callbackUrl: 'http://localhost:3333/facebook/callback'
+    }
+}
+
+export default allyConfig

+ 1 - 1
config/database.ts

@@ -46,7 +46,7 @@ const databaseConfig: DatabaseConfig = {
                 naturalSort: true
             },
             healthCheck: false,
-            debug: false
+            debug: true
         }
     }
 }

+ 1 - 1
config/drive.ts

@@ -95,7 +95,7 @@ export default driveConfig({
         */
         s3: {
             driver: 's3',
-            visibility: 'public',
+            visibility: 'private',
             key: Env.get('S3_KEY'),
             secret: Env.get('S3_SECRET'),
             region: Env.get('S3_REGION'),

+ 19 - 0
contracts/ally.ts

@@ -0,0 +1,19 @@
+/**
+ * Contract source: https://git.io/JOdiQ
+ *
+ * Feel free to let us know via PR, if you find something broken in this contract
+ * file.
+ */
+
+declare module '@ioc:Adonis/Addons/Ally' {
+	interface SocialProviders {
+    google: {
+      config: GoogleDriverConfig
+      implementation: GoogleDriverContract
+    }
+    facebook: {
+      config: FacebookDriverConfig
+      implementation: FacebookDriverContract
+    }
+	}
+}

+ 4 - 3
database/migrations/1700557040664_users.ts

@@ -6,11 +6,12 @@ export default class extends BaseSchema {
     public async up() {
         this.schema.createTable(this.tableName, (table) => {
             table.increments('id')
-            table.timestamp('created_at', { useTz: true })
-            table.timestamp('updated_at', { useTz: true })
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
+            table.enum('role', ['admin', 'user']).defaultTo('user')
             table.string('username', 80).notNullable().unique()
             table.string('phone', 120).nullable().unique()
-            table.string('email').notNullable()
+            table.string('email', 190).notNullable().unique()
             table.string('password').nullable()
             table.string('avatar').nullable()
         })

+ 4 - 3
database/migrations/1700727015979_series.ts

@@ -6,16 +6,17 @@ export default class extends BaseSchema {
     public async up() {
         this.schema.createTable(this.tableName, (table) => {
             table.increments('id')
-            table.timestamp('created_at', { useTz: true })
-            table.timestamp('updated_at', { useTz: true })
+            table.dateTime('created_at', { useTz: true })
+            table.dateTime('updated_at', { useTz: true })
             table.string('title').notNullable()
             table.string('description')
             table.string('cover')
             table.string('tags')
-            table.timestamp('release_date', { useTz: true }).nullable()
+            table.datetime('release_date', { useTz: true }).nullable()
             table.text('meta')
             table.integer('play_count').defaultTo(0)
             table.decimal('price', 19, 6).defaultTo(0)
+            table.integer('total_episodes').defaultTo(0)
         })
     }
 

+ 2 - 2
database/migrations/1700736183031_episodes.ts

@@ -6,8 +6,8 @@ export default class extends BaseSchema {
     public async up() {
         this.schema.createTable(this.tableName, (table) => {
             table.increments('id')
-            table.timestamp('created_at', { useTz: true })
-            table.timestamp('updated_at', { useTz: true })
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
             table
                 .integer('series_id')
                 .unsigned()

+ 3 - 3
database/migrations/1700738247787_trackings.ts → database/migrations/1700738247787_collections.ts

@@ -1,13 +1,13 @@
 import BaseSchema from '@ioc:Adonis/Lucid/Schema'
 
 export default class extends BaseSchema {
-    protected tableName = 'trackings'
+    protected tableName = 'collections'
 
     public async up() {
         this.schema.createTable(this.tableName, (table) => {
             table.increments('id')
-            table.timestamp('created_at', { useTz: true })
-            table.timestamp('updated_at', { useTz: true })
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
             table.integer('user_id').unsigned().references('users.id').onDelete('CASCADE')
             table.integer('series_id').unsigned().references('series.id').onDelete('CASCADE')
             table.integer('cur_ep_num').unsigned().notNullable()

+ 2 - 2
database/migrations/1700739168060_balance_records.ts

@@ -6,8 +6,8 @@ export default class extends BaseSchema {
     public async up() {
         this.schema.createTable(this.tableName, (table) => {
             table.increments('id')
-            table.timestamp('created_at', { useTz: true })
-            table.timestamp('updated_at', { useTz: true })
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
             table.integer('user_id').notNullable()
             table.decimal('amount', 19, 6).notNullable()
             table.string('type').notNullable()

+ 2 - 2
database/migrations/1700739735030_orders.ts

@@ -6,8 +6,8 @@ export default class extends BaseSchema {
     public async up() {
         this.schema.createTable(this.tableName, (table) => {
             table.increments('id')
-            table.timestamp('created_at', { useTz: true })
-            table.timestamp('updated_at', { useTz: true })
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
             table.integer('user_id')
             table.integer('price')
             table.string('type')

+ 2 - 2
database/migrations/1700812429489_user_balances.ts

@@ -6,8 +6,8 @@ export default class extends BaseSchema {
     public async up() {
         this.schema.createTable(this.tableName, (table) => {
             table.increments('id')
-            table.timestamp('created_at', { useTz: true })
-            table.timestamp('updated_at', { useTz: true })
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
             table.integer('user_id').unsigned().references('users.id').onDelete('CASCADE')
             table.decimal('balance', 19, 6).notNullable().defaultTo(0)
             table.decimal('last_balance', 19, 6).notNullable().defaultTo(0)

+ 22 - 0
database/migrations/1701165136391_categories.ts

@@ -0,0 +1,22 @@
+import BaseSchema from '@ioc:Adonis/Lucid/Schema'
+
+export default class extends BaseSchema {
+    protected tableName = 'categories'
+
+    public async up() {
+        this.schema.createTable(this.tableName, (table) => {
+            table.increments('id')
+
+            /**
+             * Uses timestamptz for PostgreSQL and DATETIME2 for MSSQL
+             */
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
+            table.string('name').notNullable()
+        })
+    }
+
+    public async down() {
+        this.schema.dropTable(this.tableName)
+    }
+}

+ 20 - 0
database/migrations/1701165722492_category_series.ts

@@ -0,0 +1,20 @@
+import BaseSchema from '@ioc:Adonis/Lucid/Schema'
+
+export default class extends BaseSchema {
+    protected tableName = 'category_series'
+
+    public async up() {
+        this.schema.createTable(this.tableName, (table) => {
+            table.increments('id').primary()
+            table.datetime('created_at', { useTz: true })
+            table.datetime('updated_at', { useTz: true })
+            table.integer('series_id').unsigned().references('series.id').onDelete('CASCADE')
+            table.integer('category_id').unsigned().references('categories.id').onDelete('CASCADE')
+            table.unique(['series_id', 'category_id'])
+        })
+    }
+
+    public async down() {
+        this.schema.dropTable(this.tableName)
+    }
+}

+ 22 - 0
database/seeders/Category.ts

@@ -0,0 +1,22 @@
+import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'
+import Category from 'App/Models/Category'
+
+export default class extends BaseSeeder {
+    public async run() {
+        // Write your database queries inside the run method
+        await Category.createMany([
+            {
+                name: '霸总'
+            },
+            {
+                name: '甜宠'
+            },
+            {
+                name: '穿越'
+            },
+            {
+                name: '复仇'
+            }
+        ])
+    }
+}

+ 20 - 0
database/seeders/File.ts

@@ -0,0 +1,20 @@
+import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'
+import Drive from '@ioc:Adonis/Core/Drive'
+import * as fs from 'fs'
+import * as path from 'path'
+export default class extends BaseSeeder {
+    public async run() {
+        // Write your database queries inside the run method
+        const dir = '/Volumes/Data HD/shortplay/files/08F2CNhR'
+        await Promise.all(
+            fs
+                .readdirSync(dir)
+                .map((file) =>
+                    Drive.putStream(
+                        path.join('files/08F2CNhR', file),
+                        fs.createReadStream(path.join(dir, file))
+                    )
+                )
+        )
+    }
+}

+ 7 - 1
database/seeders/Series.ts

@@ -10,6 +10,7 @@ export default class extends BaseSeeder {
         const dir = '/Volumes/Data HD/shortplay/downloaded/'
         for (let file of readdirSync(dir)
             .filter((file) => /^[^\.].*\.json$/.test(file))
+            .sort()
             .splice(0, 10)) {
             const data = JSON.parse(readFileSync(join(dir, file)).toString())
             console.log(data.video.title)
@@ -20,14 +21,19 @@ export default class extends BaseSeeder {
             })
 
             let i = 0
+            const eps: Episode[] = []
             for (let s of data.series) {
-                await Episode.create({
+                const ep = new Episode()
+                ep.merge({
                     seriesId: series.id,
                     episodeNum: s.sn,
                     video: s.path,
                     price: ++i > 10 ? new Decimal('99') : new Decimal('0')
                 })
+                eps.push(ep)
             }
+            await Episode.createMany(eps)
+            console.log(data.video.id)
         }
     }
 }

+ 3 - 2
database/seeders/User.ts

@@ -1,11 +1,12 @@
 import BaseSeeder from '@ioc:Adonis/Lucid/Seeder'
-import User from 'App/Models/User'
+import User, { UserRoles } from 'App/Models/User'
 
 export default class extends BaseSeeder {
     public async run() {
         await User.create({
             username: 'admin',
-            password: 'admin'
+            password: 'admin',
+            role: UserRoles.Admin
         })
     }
 }

+ 5 - 1
env.ts

@@ -31,5 +31,9 @@ export default Env.rules({
     S3_SECRET: Env.schema.string(),
     S3_BUCKET: Env.schema.string(),
     S3_REGION: Env.schema.string(),
-    S3_ENDPOINT: Env.schema.string()
+    S3_ENDPOINT: Env.schema.string(),
+    GOOGLE_CLIENT_ID: Env.schema.string(),
+    GOOGLE_CLIENT_SECRET: Env.schema.string(),
+    FACEBOOK_CLIENT_ID: Env.schema.string(),
+    FACEBOOK_CLIENT_SECRET: Env.schema.string()
 })

+ 8 - 0
package.json

@@ -58,13 +58,21 @@
         "youch-terminal": "^2.2.3"
     },
     "dependencies": {
+        "@adonisjs/ally": "^4.1.5",
         "@adonisjs/auth": "^8.2.3",
         "@adonisjs/bouncer": "^2.3.0",
         "@adonisjs/core": "^5.8.0",
         "@adonisjs/drive-s3": "^1.3.3",
         "@adonisjs/lucid": "^18.4.2",
         "@adonisjs/repl": "^3.1.0",
+        "@alicloud/openapi-client": "^0.4.7",
+        "@alicloud/sts20150401": "^1.1.4",
+        "@alicloud/tea-typescript": "^1.8.0",
+        "@alicloud/tea-util": "^1.4.7",
+        "@aws-sdk/client-s3": "^3.458.0",
+        "@aws-sdk/client-sts": "^3.458.0",
         "decimal.js": "^10.4.3",
+        "lockfile": "^1.0.4",
         "luxon": "^3.4.4",
         "mysql2": "^3.6.3",
         "proxy-addr": "^2.0.7",

+ 4 - 0
providers/AppProvider.ts

@@ -9,6 +9,10 @@ export default class AppProvider {
 
     public async boot() {
         // IoC container is ready
+        const { BaseModel } = await import('@ioc:Adonis/Lucid/Orm')
+        const CamelCaseNamingStrategy = (await import('App/Strategies/CamelCaseNamingStrategy'))
+            .default
+        BaseModel.namingStrategy = new CamelCaseNamingStrategy()
     }
 
     public async ready() {

+ 6 - 1
start/bouncer.ts

@@ -6,6 +6,7 @@
  */
 
 import Bouncer from '@ioc:Adonis/Addons/Bouncer'
+import { UserRoles } from 'App/Models/User'
 
 /*
 |--------------------------------------------------------------------------
@@ -29,7 +30,11 @@ import Bouncer from '@ioc:Adonis/Addons/Bouncer'
 | NOTE: Always export the "actions" const from this file
 |****************************************************************
 */
-export const { actions } = Bouncer
+export const { actions } = Bouncer.define('admin', (user) => {
+    return user.role === UserRoles.Admin
+}).define('owner', (user, model) => {
+    return model.userId === user.id
+})
 
 /*
 |--------------------------------------------------------------------------

+ 21 - 0
start/events.ts

@@ -0,0 +1,21 @@
+/*
+|--------------------------------------------------------------------------
+| Preloaded File
+|--------------------------------------------------------------------------
+|
+| Any code written inside this file will be executed during the application
+| boot.
+|
+*/
+import Event from '@ioc:Adonis/Core/Event'
+import Database from '@ioc:Adonis/Lucid/Database'
+import Logger from '@ioc:Adonis/Core/Logger'
+import Application from '@ioc:Adonis/Core/Application'
+
+Event.on('db:query', (query) => {
+    if (Application.inProduction) {
+        Logger.debug(query as any)
+    } else {
+        Database.prettyPrint(query)
+    }
+})

+ 28 - 4
start/routes.ts

@@ -21,8 +21,22 @@
 import Route from '@ioc:Adonis/Core/Route'
 
 Route.group(() => {
+    Route.group(() => {
+        Route.post('login', 'AuthController.login')
+        Route.post('register', 'AuthController.register')
+        Route.post('admin/login', 'AuthController.loginAdmin')
+    }).prefix('/auth')
+
+    Route.group(() => {
+        Route.post('upload', 'FilesController.store')
+        Route.get('sts', 'FilesController.sts')
+    })
+        .prefix('/files')
+        .middleware('auth:api')
+
     Route.group(() => {
         Route.get('my', 'UsersController.my')
+        Route.get('admin/my', 'UsersController.myAdmin')
     })
         .prefix('users')
         .middleware('auth:api')
@@ -31,10 +45,6 @@ Route.group(() => {
         Route.resource('users', 'UsersController').apiOnly()
     })
 
-    Route.group(() => {
-        Route.post('login', 'AuthController.login')
-    }).prefix('/auth')
-
     Route.group(() => {
         Route.resource('series', 'SeriesController').apiOnly()
     })
@@ -54,4 +64,18 @@ Route.group(() => {
     Route.group(() => {
         Route.resource('orders', 'OrdersController').apiOnly()
     }).middleware('auth:api')
+
+    Route.group(() => {
+        Route.resource('categories', 'CategoriesController').only(['index', 'show'])
+        Route.resource('categories', 'CategoriesController')
+            .apiOnly()
+            .except(['index', 'show'])
+            .middleware({
+                '*': 'auth:api'
+            })
+    })
+
+    Route.group(() => {
+        Route.resource('collections', 'CollectionsController').apiOnly()
+    }).middleware('auth:api')
 }).prefix('/api')

+ 2 - 1
tsconfig.json

@@ -32,7 +32,8 @@
       "@adonisjs/lucid",
       "@adonisjs/auth",
       "@adonisjs/bouncer",
-      "@adonisjs/drive-s3"
+      "@adonisjs/drive-s3",
+      "@adonisjs/ally"
     ]
   }
 }

+ 528 - 9
yarn.lock

@@ -22,6 +22,14 @@
     slash "^3.0.0"
     term-size "^2.2.1"
 
+"@adonisjs/ally@^4.1.5":
+  version "4.1.5"
+  resolved "https://registry.npmmirror.com/@adonisjs/ally/-/ally-4.1.5.tgz#02e787aeb0cebdc389ccb96a872113d4d8dc61f7"
+  integrity sha512-FzZySbKbGMuP7vzGAFDr0fAeOFtDiJIsa/vljk4Oq4w7NIxTD6Um67Yj4f1kbvvqlGcJlQ83WpOfA96PI2bp2g==
+  dependencies:
+    "@poppinss/oauth-client" "^4.0.2"
+    "@poppinss/utils" "^5.0.0"
+
 "@adonisjs/application@^5.2.5", "@adonisjs/application@^5.3.0":
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/@adonisjs/application/-/application-5.3.0.tgz#905d6e9fc4094c9cdc47bcbc1b887ab6ebf90c3c"
@@ -302,6 +310,90 @@
     tmp-cache "^1.1.0"
     validator "^13.11.0"
 
+"@alicloud/credentials@^2":
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/@alicloud/credentials/-/credentials-2.3.0.tgz#941233a07ba74cd2fdaa3f6a5d2a3cca5a10c184"
+  integrity sha512-x0vf/m1BzkqYXAj2Hkd22O35josx5P4VCzq/9EvTBjA7aGLX/P6JDz7QVp+gnhLjPJyvwAbErvJRYq4gIo4IMA==
+  dependencies:
+    "@alicloud/tea-typescript" "^1.5.3"
+    httpx "^2.2.0"
+    ini "^1.3.5"
+    kitx "^2.0.0"
+
+"@alicloud/endpoint-util@^0.0.1":
+  version "0.0.1"
+  resolved "https://registry.npmmirror.com/@alicloud/endpoint-util/-/endpoint-util-0.0.1.tgz#b237f5e04e373abb54c42119377b30bd6afb1a7c"
+  integrity sha512-+pH7/KEXup84cHzIL6UJAaPqETvln4yXlD9JzlrqioyCSaWxbug5FUobsiI6fuUOpw5WwoB3fWAtGbFnJ1K3Yg==
+  dependencies:
+    "@alicloud/tea-typescript" "^1.5.1"
+    kitx "^2.0.0"
+
+"@alicloud/gateway-spi@^0.0.8":
+  version "0.0.8"
+  resolved "https://registry.npmmirror.com/@alicloud/gateway-spi/-/gateway-spi-0.0.8.tgz#1d251986ed40d8b98690dcac8128fec0c56f0f53"
+  integrity sha512-KM7fu5asjxZPmrz9sJGHJeSU+cNQNOxW+SFmgmAIrITui5hXL2LB+KNRuzWmlwPjnuA2X3/keq9h6++S9jcV5g==
+  dependencies:
+    "@alicloud/credentials" "^2"
+    "@alicloud/tea-typescript" "^1.7.1"
+
+"@alicloud/openapi-client@^0.4.4", "@alicloud/openapi-client@^0.4.7":
+  version "0.4.7"
+  resolved "https://registry.npmmirror.com/@alicloud/openapi-client/-/openapi-client-0.4.7.tgz#4779e0b8b5b05431f9e59e514dee0bc451de9dfa"
+  integrity sha512-PR6ufA6hFTpB4gEpsbZpT1nSnsuOTlk9WYtAzJdMBO89ZDGc2qaJ1aCECVfeSkCVnVfTgShfYS6hmz/FjfG+5w==
+  dependencies:
+    "@alicloud/credentials" "^2"
+    "@alicloud/gateway-spi" "^0.0.8"
+    "@alicloud/openapi-util" "^0.3.2"
+    "@alicloud/tea-typescript" "^1.7.1"
+    "@alicloud/tea-util" "^1.4.7"
+    "@alicloud/tea-xml" "0.0.2"
+
+"@alicloud/openapi-util@^0.3.2":
+  version "0.3.2"
+  resolved "https://registry.npmmirror.com/@alicloud/openapi-util/-/openapi-util-0.3.2.tgz#d245e7e466d9fdf6945551c5b7c39c17a6596f1c"
+  integrity sha512-EC2JvxdcOgMlBAEG0+joOh2IB1um8CPz9EdYuRfTfd1uP8Yc9D8QRUWVGjP6scnj6fWSOaHFlit9H6PrJSyFow==
+  dependencies:
+    "@alicloud/tea-typescript" "^1.7.1"
+    "@alicloud/tea-util" "^1.3.0"
+    kitx "^2.1.0"
+    sm3 "^1.0.3"
+
+"@alicloud/sts20150401@^1.1.4":
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/@alicloud/sts20150401/-/sts20150401-1.1.4.tgz#dd6d8baa215ee51ee80ebd38d11b66b1d77677e7"
+  integrity sha512-YWkrV7286P20bB3tjlyHO8Z23qhpr2MYRE1N2ZsoOHEhpV56oIWNcrBsqT4pW9/porq49oDNtkULM1WUn3J+YA==
+  dependencies:
+    "@alicloud/endpoint-util" "^0.0.1"
+    "@alicloud/openapi-client" "^0.4.4"
+    "@alicloud/openapi-util" "^0.3.2"
+    "@alicloud/tea-typescript" "^1.7.1"
+    "@alicloud/tea-util" "^1.4.7"
+
+"@alicloud/tea-typescript@^1", "@alicloud/tea-typescript@^1.5.1", "@alicloud/tea-typescript@^1.5.3", "@alicloud/tea-typescript@^1.7.1", "@alicloud/tea-typescript@^1.8.0":
+  version "1.8.0"
+  resolved "https://registry.npmmirror.com/@alicloud/tea-typescript/-/tea-typescript-1.8.0.tgz#aa9b04b6ee53e1b22aa51e224a950ea5bcd966e9"
+  integrity sha512-CWXWaquauJf0sW30mgJRVu9aaXyBth5uMBCUc+5vKTK1zlgf3hIqRUjJZbjlwHwQ5y9anwcu18r48nOZb7l2QQ==
+  dependencies:
+    "@types/node" "^12.0.2"
+    httpx "^2.2.6"
+
+"@alicloud/tea-util@^1.3.0", "@alicloud/tea-util@^1.4.7":
+  version "1.4.7"
+  resolved "https://registry.npmmirror.com/@alicloud/tea-util/-/tea-util-1.4.7.tgz#40748613c3751f5373ffa8e5a0e892602ef3f78c"
+  integrity sha512-Lrpfk9kxihHsit3oMoeIMjk783AxjOvzMhLAbZcIzazKiVg3Zk/209XDe9r1lXqxII59j3V4rhC9X14y6WGYyg==
+  dependencies:
+    "@alicloud/tea-typescript" "^1.5.1"
+    kitx "^2.0.0"
+
+"@alicloud/tea-xml@0.0.2":
+  version "0.0.2"
+  resolved "https://registry.npmmirror.com/@alicloud/tea-xml/-/tea-xml-0.0.2.tgz#7c97a38255d5e4f009c437facd3a2afc0ef17f45"
+  integrity sha512-Xs7v5y7YSNSDDYmiDWAC0/013VWPjS3dQU4KezSLva9VGiTVPaL3S7Nk4NrTmAYCG6MKcrRj/nGEDIWL5KRoPg==
+  dependencies:
+    "@alicloud/tea-typescript" "^1"
+    "@types/xml2js" "^0.4.5"
+    xml2js "^0.4.22"
+
 "@apidevtools/json-schema-ref-parser@^9.0.6":
   version "9.1.2"
   resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz#8ff5386b365d4c9faa7c8b566ff16a46a577d9b8"
@@ -479,6 +571,69 @@
     fast-xml-parser "4.2.5"
     tslib "^2.5.0"
 
+"@aws-sdk/client-s3@^3.458.0":
+  version "3.458.0"
+  resolved "https://registry.npmmirror.com/@aws-sdk/client-s3/-/client-s3-3.458.0.tgz#3e60c0c790398cf4ca6862254fff5a2aaf102873"
+  integrity sha512-MTRhiBLGjCd3x3fPpDpXf6fjBsaX+jvh9vSv1S5ulgOR+z2vbUOViXHNTpuHcgOJ3TnnSP5/voHr7K8NJHUksg==
+  dependencies:
+    "@aws-crypto/sha1-browser" "3.0.0"
+    "@aws-crypto/sha256-browser" "3.0.0"
+    "@aws-crypto/sha256-js" "3.0.0"
+    "@aws-sdk/client-sts" "3.458.0"
+    "@aws-sdk/core" "3.451.0"
+    "@aws-sdk/credential-provider-node" "3.458.0"
+    "@aws-sdk/middleware-bucket-endpoint" "3.451.0"
+    "@aws-sdk/middleware-expect-continue" "3.451.0"
+    "@aws-sdk/middleware-flexible-checksums" "3.451.0"
+    "@aws-sdk/middleware-host-header" "3.451.0"
+    "@aws-sdk/middleware-location-constraint" "3.451.0"
+    "@aws-sdk/middleware-logger" "3.451.0"
+    "@aws-sdk/middleware-recursion-detection" "3.451.0"
+    "@aws-sdk/middleware-sdk-s3" "3.451.0"
+    "@aws-sdk/middleware-signing" "3.451.0"
+    "@aws-sdk/middleware-ssec" "3.451.0"
+    "@aws-sdk/middleware-user-agent" "3.451.0"
+    "@aws-sdk/region-config-resolver" "3.451.0"
+    "@aws-sdk/signature-v4-multi-region" "3.451.0"
+    "@aws-sdk/types" "3.451.0"
+    "@aws-sdk/util-endpoints" "3.451.0"
+    "@aws-sdk/util-user-agent-browser" "3.451.0"
+    "@aws-sdk/util-user-agent-node" "3.451.0"
+    "@aws-sdk/xml-builder" "3.310.0"
+    "@smithy/config-resolver" "^2.0.18"
+    "@smithy/eventstream-serde-browser" "^2.0.13"
+    "@smithy/eventstream-serde-config-resolver" "^2.0.13"
+    "@smithy/eventstream-serde-node" "^2.0.13"
+    "@smithy/fetch-http-handler" "^2.2.6"
+    "@smithy/hash-blob-browser" "^2.0.14"
+    "@smithy/hash-node" "^2.0.15"
+    "@smithy/hash-stream-node" "^2.0.15"
+    "@smithy/invalid-dependency" "^2.0.13"
+    "@smithy/md5-js" "^2.0.15"
+    "@smithy/middleware-content-length" "^2.0.15"
+    "@smithy/middleware-endpoint" "^2.2.0"
+    "@smithy/middleware-retry" "^2.0.20"
+    "@smithy/middleware-serde" "^2.0.13"
+    "@smithy/middleware-stack" "^2.0.7"
+    "@smithy/node-config-provider" "^2.1.5"
+    "@smithy/node-http-handler" "^2.1.9"
+    "@smithy/protocol-http" "^3.0.9"
+    "@smithy/smithy-client" "^2.1.15"
+    "@smithy/types" "^2.5.0"
+    "@smithy/url-parser" "^2.0.13"
+    "@smithy/util-base64" "^2.0.1"
+    "@smithy/util-body-length-browser" "^2.0.0"
+    "@smithy/util-body-length-node" "^2.1.0"
+    "@smithy/util-defaults-mode-browser" "^2.0.19"
+    "@smithy/util-defaults-mode-node" "^2.0.25"
+    "@smithy/util-endpoints" "^1.0.4"
+    "@smithy/util-retry" "^2.0.6"
+    "@smithy/util-stream" "^2.0.20"
+    "@smithy/util-utf8" "^2.0.2"
+    "@smithy/util-waiter" "^2.0.13"
+    fast-xml-parser "4.2.5"
+    tslib "^2.5.0"
+
 "@aws-sdk/client-sso@3.451.0":
   version "3.451.0"
   resolved "https://registry.npmmirror.com/@aws-sdk/client-sso/-/client-sso-3.451.0.tgz#d52b961efa707b6579821942801145a2e1be8121"
@@ -521,6 +676,48 @@
     "@smithy/util-utf8" "^2.0.2"
     tslib "^2.5.0"
 
+"@aws-sdk/client-sso@3.458.0":
+  version "3.458.0"
+  resolved "https://registry.npmmirror.com/@aws-sdk/client-sso/-/client-sso-3.458.0.tgz#f40d3556faa6bad60819eb4cd28f0cc45e80f23a"
+  integrity sha512-GTiIH4So0PTU5oAldtOacO/cBonu4NWGfvN3+BUaAb5Ybb9yQiwcO08PS/pXZ0cw4UTVK+zr22WVLR0reomUTA==
+  dependencies:
+    "@aws-crypto/sha256-browser" "3.0.0"
+    "@aws-crypto/sha256-js" "3.0.0"
+    "@aws-sdk/core" "3.451.0"
+    "@aws-sdk/middleware-host-header" "3.451.0"
+    "@aws-sdk/middleware-logger" "3.451.0"
+    "@aws-sdk/middleware-recursion-detection" "3.451.0"
+    "@aws-sdk/middleware-user-agent" "3.451.0"
+    "@aws-sdk/region-config-resolver" "3.451.0"
+    "@aws-sdk/types" "3.451.0"
+    "@aws-sdk/util-endpoints" "3.451.0"
+    "@aws-sdk/util-user-agent-browser" "3.451.0"
+    "@aws-sdk/util-user-agent-node" "3.451.0"
+    "@smithy/config-resolver" "^2.0.18"
+    "@smithy/fetch-http-handler" "^2.2.6"
+    "@smithy/hash-node" "^2.0.15"
+    "@smithy/invalid-dependency" "^2.0.13"
+    "@smithy/middleware-content-length" "^2.0.15"
+    "@smithy/middleware-endpoint" "^2.2.0"
+    "@smithy/middleware-retry" "^2.0.20"
+    "@smithy/middleware-serde" "^2.0.13"
+    "@smithy/middleware-stack" "^2.0.7"
+    "@smithy/node-config-provider" "^2.1.5"
+    "@smithy/node-http-handler" "^2.1.9"
+    "@smithy/protocol-http" "^3.0.9"
+    "@smithy/smithy-client" "^2.1.15"
+    "@smithy/types" "^2.5.0"
+    "@smithy/url-parser" "^2.0.13"
+    "@smithy/util-base64" "^2.0.1"
+    "@smithy/util-body-length-browser" "^2.0.0"
+    "@smithy/util-body-length-node" "^2.1.0"
+    "@smithy/util-defaults-mode-browser" "^2.0.19"
+    "@smithy/util-defaults-mode-node" "^2.0.25"
+    "@smithy/util-endpoints" "^1.0.4"
+    "@smithy/util-retry" "^2.0.6"
+    "@smithy/util-utf8" "^2.0.2"
+    tslib "^2.5.0"
+
 "@aws-sdk/client-sts@3.454.0":
   version "3.454.0"
   resolved "https://registry.npmmirror.com/@aws-sdk/client-sts/-/client-sts-3.454.0.tgz#6106999e393c264a485fc76add374b375a2da8d5"
@@ -567,6 +764,52 @@
     fast-xml-parser "4.2.5"
     tslib "^2.5.0"
 
+"@aws-sdk/client-sts@3.458.0", "@aws-sdk/client-sts@^3.458.0":
+  version "3.458.0"
+  resolved "https://registry.npmmirror.com/@aws-sdk/client-sts/-/client-sts-3.458.0.tgz#74b763093dd7ce10b8d5b06d4019cac8d47feea5"
+  integrity sha512-c34zmQxcP7AM62S7SAiOztxTaHJOG+0aIb2GYUeag5sQzG7FnGGwZ7hA0ggCQc7iMkDL9UYHKKtLs1ynQenW+A==
+  dependencies:
+    "@aws-crypto/sha256-browser" "3.0.0"
+    "@aws-crypto/sha256-js" "3.0.0"
+    "@aws-sdk/core" "3.451.0"
+    "@aws-sdk/credential-provider-node" "3.458.0"
+    "@aws-sdk/middleware-host-header" "3.451.0"
+    "@aws-sdk/middleware-logger" "3.451.0"
+    "@aws-sdk/middleware-recursion-detection" "3.451.0"
+    "@aws-sdk/middleware-sdk-sts" "3.451.0"
+    "@aws-sdk/middleware-signing" "3.451.0"
+    "@aws-sdk/middleware-user-agent" "3.451.0"
+    "@aws-sdk/region-config-resolver" "3.451.0"
+    "@aws-sdk/types" "3.451.0"
+    "@aws-sdk/util-endpoints" "3.451.0"
+    "@aws-sdk/util-user-agent-browser" "3.451.0"
+    "@aws-sdk/util-user-agent-node" "3.451.0"
+    "@smithy/config-resolver" "^2.0.18"
+    "@smithy/fetch-http-handler" "^2.2.6"
+    "@smithy/hash-node" "^2.0.15"
+    "@smithy/invalid-dependency" "^2.0.13"
+    "@smithy/middleware-content-length" "^2.0.15"
+    "@smithy/middleware-endpoint" "^2.2.0"
+    "@smithy/middleware-retry" "^2.0.20"
+    "@smithy/middleware-serde" "^2.0.13"
+    "@smithy/middleware-stack" "^2.0.7"
+    "@smithy/node-config-provider" "^2.1.5"
+    "@smithy/node-http-handler" "^2.1.9"
+    "@smithy/protocol-http" "^3.0.9"
+    "@smithy/smithy-client" "^2.1.15"
+    "@smithy/types" "^2.5.0"
+    "@smithy/url-parser" "^2.0.13"
+    "@smithy/util-base64" "^2.0.1"
+    "@smithy/util-body-length-browser" "^2.0.0"
+    "@smithy/util-body-length-node" "^2.1.0"
+    "@smithy/util-defaults-mode-browser" "^2.0.19"
+    "@smithy/util-defaults-mode-node" "^2.0.25"
+    "@smithy/util-endpoints" "^1.0.4"
+    "@smithy/util-retry" "^2.0.6"
+    "@smithy/util-utf8" "^2.0.2"
+    fast-xml-parser "4.2.5"
+    tslib "^2.5.0"
+
 "@aws-sdk/core@3.451.0":
   version "3.451.0"
   resolved "https://registry.npmmirror.com/@aws-sdk/core/-/core-3.451.0.tgz#ecd30da40d8e02050a772920485f450ea2a1b804"
@@ -601,6 +844,22 @@
     "@smithy/types" "^2.5.0"
     tslib "^2.5.0"
 
+"@aws-sdk/credential-provider-ini@3.458.0":
+  version "3.458.0"
+  resolved "https://registry.npmmirror.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.458.0.tgz#b37ca1740a87c169ad590e84cfc7d3e84106c673"
+  integrity sha512-M293Im4k6QrKlWaPfElYKRPlBXMaXbkqns4YgLGBpabfIVIZEguGj/kVm7gkEKdt8rCHbBqqXgsQrtQbfDkkBQ==
+  dependencies:
+    "@aws-sdk/credential-provider-env" "3.451.0"
+    "@aws-sdk/credential-provider-process" "3.451.0"
+    "@aws-sdk/credential-provider-sso" "3.458.0"
+    "@aws-sdk/credential-provider-web-identity" "3.451.0"
+    "@aws-sdk/types" "3.451.0"
+    "@smithy/credential-provider-imds" "^2.0.0"
+    "@smithy/property-provider" "^2.0.0"
+    "@smithy/shared-ini-file-loader" "^2.0.6"
+    "@smithy/types" "^2.5.0"
+    tslib "^2.5.0"
+
 "@aws-sdk/credential-provider-node@3.451.0":
   version "3.451.0"
   resolved "https://registry.npmmirror.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.451.0.tgz#72ccdef2199104379977dc06ea84c8d2a356d545"
@@ -618,6 +877,23 @@
     "@smithy/types" "^2.5.0"
     tslib "^2.5.0"
 
+"@aws-sdk/credential-provider-node@3.458.0":
+  version "3.458.0"
+  resolved "https://registry.npmmirror.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.458.0.tgz#a68b7bcbb28f49f6e6dd848590a263f0a462e503"
+  integrity sha512-psNXL3/GAoDAqRusdy5umglXTOvxtE9XQTtmOPn4O/H2NpXqe+cB2/W+H+uikgyyck9Lu4DwMk+voFz2Hl8znw==
+  dependencies:
+    "@aws-sdk/credential-provider-env" "3.451.0"
+    "@aws-sdk/credential-provider-ini" "3.458.0"
+    "@aws-sdk/credential-provider-process" "3.451.0"
+    "@aws-sdk/credential-provider-sso" "3.458.0"
+    "@aws-sdk/credential-provider-web-identity" "3.451.0"
+    "@aws-sdk/types" "3.451.0"
+    "@smithy/credential-provider-imds" "^2.0.0"
+    "@smithy/property-provider" "^2.0.0"
+    "@smithy/shared-ini-file-loader" "^2.0.6"
+    "@smithy/types" "^2.5.0"
+    tslib "^2.5.0"
+
 "@aws-sdk/credential-provider-process@3.451.0":
   version "3.451.0"
   resolved "https://registry.npmmirror.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.451.0.tgz#3dd1d7df235f4eeb99d7e0f16b0e8cd61d555a73"
@@ -642,6 +918,19 @@
     "@smithy/types" "^2.5.0"
     tslib "^2.5.0"
 
+"@aws-sdk/credential-provider-sso@3.458.0":
+  version "3.458.0"
+  resolved "https://registry.npmmirror.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.458.0.tgz#4634c0c67656b4963a3db7ae65ca7fc646f040ae"
+  integrity sha512-dyRAKvMLF9Ur6M0YtWSU4E5YDVEFO7Rfg5FOTk+Lwnk24UQ0RoHg3c6HZ8sPTNx16cgx4YY68UYi/HTZf56z2g==
+  dependencies:
+    "@aws-sdk/client-sso" "3.458.0"
+    "@aws-sdk/token-providers" "3.451.0"
+    "@aws-sdk/types" "3.451.0"
+    "@smithy/property-provider" "^2.0.0"
+    "@smithy/shared-ini-file-loader" "^2.0.6"
+    "@smithy/types" "^2.5.0"
+    tslib "^2.5.0"
+
 "@aws-sdk/credential-provider-web-identity@3.451.0":
   version "3.451.0"
   resolved "https://registry.npmmirror.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.451.0.tgz#5dc40768869d5887888c6f178c7831dd2c74cfbe"
@@ -1268,6 +1557,15 @@
     safe-buffer "5.2.1"
     uid-safe "2.1.5"
 
+"@poppinss/oauth-client@^4.0.2":
+  version "4.0.2"
+  resolved "https://registry.npmmirror.com/@poppinss/oauth-client/-/oauth-client-4.0.2.tgz#82918d476c40a74e65f9397b657bb3306c54848b"
+  integrity sha512-xLP0oQCxhFQlOrLa3KOdJGIP8dGJKMJstaMDshB0f0L/w18oZXqQFUT2djFHO7XaxqpMyWyOUZKjEt0RGlw57Q==
+  dependencies:
+    "@types/luxon" "^2.3.1"
+    got "^11.8.2"
+    luxon "^2.3.1"
+
 "@poppinss/prompts@^2.0.2":
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/@poppinss/prompts/-/prompts-2.0.2.tgz#2455104779876b4772871e7ac53ee53424eb1f02"
@@ -1327,6 +1625,11 @@
   resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
   integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
 
+"@sindresorhus/is@^4.0.0":
+  version "4.6.0"
+  resolved "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
+  integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
+
 "@smithy/abort-controller@^2.0.1", "@smithy/abort-controller@^2.0.13":
   version "2.0.13"
   resolved "https://registry.npmmirror.com/@smithy/abort-controller/-/abort-controller-2.0.13.tgz#d050a969bf1a478e548a323ea0f1b83532cbc136"
@@ -1824,6 +2127,13 @@
     "@smithy/types" "^2.5.0"
     tslib "^2.5.0"
 
+"@szmarczak/http-timer@^4.0.5":
+  version "4.0.6"
+  resolved "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
+  integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==
+  dependencies:
+    defer-to-connect "^2.0.0"
+
 "@tokenizer/token@^0.3.0":
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
@@ -1834,6 +2144,16 @@
   resolved "https://registry.yarnpkg.com/@types/bytes/-/bytes-3.1.4.tgz#8563f38ea6096df3f409c6500e8ac171790a7c1f"
   integrity sha512-A0uYgOj3zNc4hNjHc5lYUfJQ/HVyBXiUMKdXd7ysclaE6k9oJdavQzODHuwjpUu2/boCP8afjQYi8z/GtvNCWA==
 
+"@types/cacheable-request@^6.0.1":
+  version "6.0.3"
+  resolved "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183"
+  integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==
+  dependencies:
+    "@types/http-cache-semantics" "*"
+    "@types/keyv" "^3.1.4"
+    "@types/node" "*"
+    "@types/responselike" "^1.0.0"
+
 "@types/chai@^4.3.4":
   version "4.3.11"
   resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.11.tgz#e95050bf79a932cb7305dd130254ccdf9bde671c"
@@ -1864,6 +2184,11 @@
   resolved "https://registry.yarnpkg.com/@types/he/-/he-1.2.3.tgz#c33ca3096f30cbd5d68d78211572de3f9adff75a"
   integrity sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==
 
+"@types/http-cache-semantics@*":
+  version "4.0.4"
+  resolved "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4"
+  integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==
+
 "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
@@ -1889,6 +2214,18 @@
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
   integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
 
+"@types/keyv@^3.1.4":
+  version "3.1.4"
+  resolved "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
+  integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
+  dependencies:
+    "@types/node" "*"
+
+"@types/luxon@^2.3.1":
+  version "2.4.0"
+  resolved "https://registry.npmmirror.com/@types/luxon/-/luxon-2.4.0.tgz#897d3abc23b68d78b69d76a12c21e01eb5adab95"
+  integrity sha512-oCavjEjRXuR6URJEtQm0eBdfsBiEcGBZbq21of8iGkeKxU1+1xgKuFPClaBZl2KB8ZZBSWlgk61tH6Mf+nvZVw==
+
 "@types/luxon@^3.3.1":
   version "3.3.5"
   resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.5.tgz#ffdcec196994998dbef6284523b3ac88a9e6c45f"
@@ -1906,6 +2243,18 @@
   dependencies:
     undici-types "~5.26.4"
 
+"@types/node@^12.0.2":
+  version "12.20.55"
+  resolved "https://registry.npmmirror.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
+  integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
+
+"@types/node@^20":
+  version "20.10.0"
+  resolved "https://registry.npmmirror.com/@types/node/-/node-20.10.0.tgz#16ddf9c0a72b832ec4fcce35b8249cf149214617"
+  integrity sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==
+  dependencies:
+    undici-types "~5.26.4"
+
 "@types/pino-pretty@*":
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/@types/pino-pretty/-/pino-pretty-5.0.0.tgz#aa7a61cfd553b051764acfa0a49872f7a09a1722"
@@ -1937,6 +2286,13 @@
   dependencies:
     "@types/node" "*"
 
+"@types/responselike@^1.0.0":
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50"
+  integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==
+  dependencies:
+    "@types/node" "*"
+
 "@types/semver@^7.3.12":
   version "7.5.6"
   resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339"
@@ -1962,6 +2318,13 @@
   resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.11.7.tgz#99e19760297667ae46b7069ec8b96cbfe0a08b98"
   integrity sha512-q0JomTsJ2I5Mv7dhHhQLGjMvX0JJm5dyZ1DXQySIUzU1UlwzB8bt+R6+LODUbz0UDIOvEzGc28tk27gBJw2N8Q==
 
+"@types/xml2js@^0.4.5":
+  version "0.4.14"
+  resolved "https://registry.npmmirror.com/@types/xml2js/-/xml2js-0.4.14.tgz#5d462a2a7330345e2309c6b549a183a376de8f9a"
+  integrity sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==
+  dependencies:
+    "@types/node" "*"
+
 "@types/yargs-parser@*":
   version "21.0.3"
   resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
@@ -2488,6 +2851,24 @@ cache-base@^1.0.1:
     union-value "^1.0.0"
     unset-value "^1.0.0"
 
+cacheable-lookup@^5.0.3:
+  version "5.0.4"
+  resolved "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
+  integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
+
+cacheable-request@^7.0.2:
+  version "7.0.4"
+  resolved "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817"
+  integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==
+  dependencies:
+    clone-response "^1.0.2"
+    get-stream "^5.1.0"
+    http-cache-semantics "^4.0.0"
+    keyv "^4.0.0"
+    lowercase-keys "^2.0.0"
+    normalize-url "^6.0.1"
+    responselike "^2.0.0"
+
 call-bind@^1.0.0:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"
@@ -2667,6 +3048,13 @@ clone-deep@^4.0.1:
     kind-of "^6.0.2"
     shallow-clone "^3.0.0"
 
+clone-response@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3"
+  integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==
+  dependencies:
+    mimic-response "^1.0.0"
+
 clone@^1.0.2:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
@@ -2891,12 +3279,7 @@ debug@4.3.4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
   dependencies:
     ms "2.1.2"
 
-decimal.js@^10.3.1:
-  version "10.4.3"
-  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
-  integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
-
-decimal.js@^10.4.3:
+decimal.js@^10.3.1, decimal.js@^10.4.3:
   version "10.4.3"
   resolved "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
   integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
@@ -2906,6 +3289,13 @@ decode-uri-component@^0.2.0:
   resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
   integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
 
+decompress-response@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
+  integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
+  dependencies:
+    mimic-response "^3.1.0"
+
 deep-eql@^4.1.3:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
@@ -2948,6 +3338,11 @@ defaults@^1.0.3:
   dependencies:
     clone "^1.0.2"
 
+defer-to-connect@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
+  integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
+
 define-data-property@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"
@@ -3659,6 +4054,13 @@ get-source@^2.0.12:
     data-uri-to-buffer "^2.0.0"
     source-map "^0.6.1"
 
+get-stream@^5.1.0:
+  version "5.2.0"
+  resolved "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+  integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
+  dependencies:
+    pump "^3.0.0"
+
 get-stream@^6.0.0, get-stream@^6.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
@@ -3764,6 +4166,23 @@ gopd@^1.0.1:
   dependencies:
     get-intrinsic "^1.1.3"
 
+got@^11.8.2:
+  version "11.8.6"
+  resolved "https://registry.npmmirror.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
+  integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==
+  dependencies:
+    "@sindresorhus/is" "^4.0.0"
+    "@szmarczak/http-timer" "^4.0.5"
+    "@types/cacheable-request" "^6.0.1"
+    "@types/responselike" "^1.0.0"
+    cacheable-lookup "^5.0.3"
+    cacheable-request "^7.0.2"
+    decompress-response "^6.0.0"
+    http2-wrapper "^1.0.0-beta.5.2"
+    lowercase-keys "^2.0.0"
+    p-cancelable "^2.0.0"
+    responselike "^2.0.0"
+
 graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
   version "4.2.11"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
@@ -3889,6 +4308,11 @@ hexoid@^1.0.0:
   resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18"
   integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==
 
+http-cache-semantics@^4.0.0:
+  version "4.1.1"
+  resolved "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
+  integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
+
 http-errors@2.0.0, http-errors@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
@@ -3900,6 +4324,22 @@ http-errors@2.0.0, http-errors@^2.0.0:
     statuses "2.0.1"
     toidentifier "1.0.1"
 
+http2-wrapper@^1.0.0-beta.5.2:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
+  integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
+  dependencies:
+    quick-lru "^5.1.1"
+    resolve-alpn "^1.0.0"
+
+httpx@^2.2.0, httpx@^2.2.6:
+  version "2.3.1"
+  resolved "https://registry.npmmirror.com/httpx/-/httpx-2.3.1.tgz#e4c7a07dd7f9458810f3ae80eb13e3afcc75500b"
+  integrity sha512-l5rcAoKP8A9XOIlcIA87Wt9A7AX2fgOslHOF4WB5Q24y/1+aeH8b7c7NKfm+Bcf+h0u4FHNtLCriC4mAFmCYgg==
+  dependencies:
+    "@types/node" "^20"
+    debug "^4.1.1"
+
 human-signals@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
@@ -3987,7 +4427,7 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.4:
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
 
-ini@~1.3.0:
+ini@^1.3.5, ini@~1.3.0:
   version "1.3.8"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
   integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
@@ -4327,7 +4767,7 @@ junk@^3.1.0:
   resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
   integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
 
-keyv@^4.5.3:
+keyv@^4.0.0, keyv@^4.5.3:
   version "4.5.4"
   resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
   integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
@@ -4353,6 +4793,13 @@ kind-of@^6.0.2, kind-of@^6.0.3:
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
   integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
 
+kitx@^2.0.0, kitx@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/kitx/-/kitx-2.1.0.tgz#fc7fbf78eb6ed7a5a3fd2d7afb3011e29d0e44c8"
+  integrity sha512-C/5v9MtIX7aHGOjwn5BmrrbNkJSf7i0R5mRzmh13GSAdRqQ7bYQo/Su2pTYNylFicqKNTVX3HML9k1u8k51+pQ==
+  dependencies:
+    "@types/node" "^12.0.2"
+
 kleur@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
@@ -4443,6 +4890,13 @@ locate-path@^6.0.0:
   dependencies:
     p-locate "^5.0.0"
 
+lockfile@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.npmmirror.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609"
+  integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==
+  dependencies:
+    signal-exit "^3.0.2"
+
 lodash.flatten@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
@@ -4502,6 +4956,11 @@ lower-case@^2.0.2:
   dependencies:
     tslib "^2.0.3"
 
+lowercase-keys@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
+  integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+
 lru-cache@^4.1.5:
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -4527,6 +4986,11 @@ lru-cache@^8.0.0:
   resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-8.0.5.tgz#983fe337f3e176667f8e567cfcce7cb064ea214e"
   integrity sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==
 
+luxon@^2.3.1:
+  version "2.5.2"
+  resolved "https://registry.npmmirror.com/luxon/-/luxon-2.5.2.tgz#17ed497f0277e72d58a4756d6a9abee4681457b6"
+  integrity sha512-Yg7/RDp4nedqmLgyH0LwgGRvMEKVzKbUdkBYyCosbHgJ+kaOUx0qzSiSatVc3DFygnirTPYnMM2P5dg2uH1WvA==
+
 luxon@^3.0.4, luxon@^3.3.0, luxon@^3.4.1, luxon@^3.4.4:
   version "3.4.4"
   resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af"
@@ -4698,6 +5162,16 @@ mimic-fn@^4.0.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
   integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
 
+mimic-response@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
+  integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
+
+mimic-response@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
+  integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
+
 minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
@@ -4860,7 +5334,7 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
   integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
 
-normalize-url@^6.1.0:
+normalize-url@^6.0.1, normalize-url@^6.1.0:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
   integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
@@ -4993,6 +5467,11 @@ p-all@^2.1.0:
   dependencies:
     p-map "^2.0.0"
 
+p-cancelable@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
+  integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
+
 p-defer@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
@@ -5381,6 +5860,11 @@ quick-format-unescaped@^4.0.3:
   resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7"
   integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==
 
+quick-lru@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
+  integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
+
 random-bytes@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
@@ -5504,6 +5988,11 @@ require-all@^3.0.0:
   resolved "https://registry.yarnpkg.com/require-all/-/require-all-3.0.0.tgz#473d49704be310115ce124f77383b1ebd8671312"
   integrity sha512-jPGN876lc5exWYrMcgZSd7U42P0PmVQzxnQB13fCSzmyGnqQWW4WUz5DosZ/qe24hz+5o9lSvW2epBNZ1xa6Fw==
 
+resolve-alpn@^1.0.0:
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
+  integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==
+
 resolve-from@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -5528,6 +6017,13 @@ resolve@^1.20.0:
     path-parse "^1.0.7"
     supports-preserve-symlinks-flag "^1.0.0"
 
+responselike@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc"
+  integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==
+  dependencies:
+    lowercase-keys "^2.0.0"
+
 restore-cursor@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
@@ -5594,6 +6090,11 @@ safe-regex@^1.1.0:
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
 
+sax@>=0.6.0:
+  version "1.3.0"
+  resolved "https://registry.npmmirror.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0"
+  integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==
+
 secure-json-parse@^2.4.0:
   version "2.7.0"
   resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862"
@@ -5756,6 +6257,11 @@ slugify@^1.6.5:
   resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.6.tgz#2d4ac0eacb47add6af9e04d3be79319cbcc7924b"
   integrity sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==
 
+sm3@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/sm3/-/sm3-1.0.3.tgz#0051f0cc948c983944843136e7baa244eec5cd49"
+  integrity sha512-KyFkIfr8QBlFG3uc3NaljaXdYcsbRy1KrSfc4tsQV8jW68jAktGeOcifu530Vx/5LC+PULHT0Rv8LiI8Gw+c1g==
+
 smpltmpl@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/smpltmpl/-/smpltmpl-1.0.2.tgz#b6e9d0eedab7827455c46c98fb8b9505ae6d4a82"
@@ -6358,6 +6864,19 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
+xml2js@^0.4.22:
+  version "0.4.23"
+  resolved "https://registry.npmmirror.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
+  integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
+  dependencies:
+    sax ">=0.6.0"
+    xmlbuilder "~11.0.0"
+
+xmlbuilder@~11.0.0:
+  version "11.0.1"
+  resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
+  integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
+
 yallist@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"