From 1bfc72f9cb3748514d084864a0f72882080c3e30 Mon Sep 17 00:00:00 2001 From: fanhongcai Date: Mon, 24 Aug 2026 00:03:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=87=E4=BC=A0=E6=98=93=E5=8C=BF?= =?UTF-8?q?=E5=90=8D=E4=B8=B4=E6=97=B6=E6=96=87=E4=BB=B6=E4=BC=A0=E8=BE=93?= =?UTF-8?q?=E7=AB=99=E5=88=9D=E5=A7=8B=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 .NET 8 + FreeSql + 阿里云 OSS,支持共享/私密/标签三种文件模式 - 前端 Vue3 + Vite + TDesign,上传/取件/管理三页 - 取件码/二维码/海报合成(含保存二维码为图片) - deploy/ 部署文档与一键 FTP 发布脚本 - appsettings.json 含真实凭据,已 gitignore 不入库 --- .gitignore | 26 + backend/WenChuanyi.Api/.gitignore | 5 + .../Controllers/AdminController.cs | 92 + .../Controllers/FileController.cs | 392 ++ .../Controllers/OpenApiController.cs | 156 + backend/WenChuanyi.Api/Dtos/FileInfoDto.cs | 22 + backend/WenChuanyi.Api/Dtos/OpenApiDtos.cs | 49 + .../WenChuanyi.Api/Dtos/UploadResultDto.cs | 17 + backend/WenChuanyi.Api/Models/FileItem.cs | 63 + backend/WenChuanyi.Api/Program.cs | 49 + .../Properties/launchSettings.json | 31 + .../Services/CodeGeneratorService.cs | 57 + .../Services/ExpiredFileCleanerService.cs | 67 + backend/WenChuanyi.Api/Services/FileRules.cs | 108 + .../Services/OssStorageService.cs | 68 + backend/WenChuanyi.Api/WenChuanyi.Api.csproj | 14 + backend/WenChuanyi.Api/WenChuanyi.Api.http | 6 + .../WenChuanyi.Api/appsettings.example.json | 39 + deploy/IIS部署与FTP发布.md | 163 + deploy/ftp_force_one.ps1 | 20 + deploy/ftp_upload.ps1 | 59 + deploy/publish.ps1 | 119 + frontend/.gitignore | 4 + frontend/index.html | 20 + frontend/package-lock.json | 3160 +++++++++++++++++ frontend/package.json | 28 + frontend/postcss.config.js | 6 + frontend/src/App.vue | 100 + frontend/src/api/index.ts | 75 + frontend/src/env.d.ts | 7 + frontend/src/index.css | 64 + frontend/src/main.ts | 8 + frontend/src/router/index.ts | 13 + frontend/src/views/AdminView.vue | 145 + frontend/src/views/PickupView.vue | 450 +++ frontend/src/views/UploadView.vue | 720 ++++ frontend/tailwind.config.js | 36 + frontend/tsconfig.json | 22 + frontend/vite.config.ts | 28 + 需求文档.md | 341 ++ 40 files changed, 6849 insertions(+) create mode 100644 .gitignore create mode 100644 backend/WenChuanyi.Api/.gitignore create mode 100644 backend/WenChuanyi.Api/Controllers/AdminController.cs create mode 100644 backend/WenChuanyi.Api/Controllers/FileController.cs create mode 100644 backend/WenChuanyi.Api/Controllers/OpenApiController.cs create mode 100644 backend/WenChuanyi.Api/Dtos/FileInfoDto.cs create mode 100644 backend/WenChuanyi.Api/Dtos/OpenApiDtos.cs create mode 100644 backend/WenChuanyi.Api/Dtos/UploadResultDto.cs create mode 100644 backend/WenChuanyi.Api/Models/FileItem.cs create mode 100644 backend/WenChuanyi.Api/Program.cs create mode 100644 backend/WenChuanyi.Api/Properties/launchSettings.json create mode 100644 backend/WenChuanyi.Api/Services/CodeGeneratorService.cs create mode 100644 backend/WenChuanyi.Api/Services/ExpiredFileCleanerService.cs create mode 100644 backend/WenChuanyi.Api/Services/FileRules.cs create mode 100644 backend/WenChuanyi.Api/Services/OssStorageService.cs create mode 100644 backend/WenChuanyi.Api/WenChuanyi.Api.csproj create mode 100644 backend/WenChuanyi.Api/WenChuanyi.Api.http create mode 100644 backend/WenChuanyi.Api/appsettings.example.json create mode 100644 deploy/IIS部署与FTP发布.md create mode 100644 deploy/ftp_force_one.ps1 create mode 100644 deploy/ftp_upload.ps1 create mode 100644 deploy/publish.ps1 create mode 100644 frontend/.gitignore create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.js create mode 100644 frontend/src/App.vue create mode 100644 frontend/src/api/index.ts create mode 100644 frontend/src/env.d.ts create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.ts create mode 100644 frontend/src/router/index.ts create mode 100644 frontend/src/views/AdminView.vue create mode 100644 frontend/src/views/PickupView.vue create mode 100644 frontend/src/views/UploadView.vue create mode 100644 frontend/tailwind.config.js create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 需求文档.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae6ea8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# ============ 构建产物 ============ +node_modules/ +frontend/dist/ +backend/WenChuanyi.Api/bin/ +backend/WenChuanyi.Api/obj/ + +# ============ 敏感配置(含真实凭据,不入库) ============ +backend/WenChuanyi.Api/appsettings.json +backend/WenChuanyi.Api/appsettings.Development.json + +# ============ IDE / 工具 / 缓存 ============ +.vscode/ +.codebuddy/ +.playwright-cli/ +snapshot_tmp.txt + +# ============ 临时 ============ +*.log +*.tmp +ftp_upload.log +ftp_upload.err +site_index.html +backend/WenChuanyi.Api/run.err +backend/WenChuanyi.Api/run.out.log +frontend/vite.config.ts.timestamp-*.mjs +frontend/dist/ diff --git a/backend/WenChuanyi.Api/.gitignore b/backend/WenChuanyi.Api/.gitignore new file mode 100644 index 0000000..6e2900a --- /dev/null +++ b/backend/WenChuanyi.Api/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +appsettings.json +*.user +.vs/ diff --git a/backend/WenChuanyi.Api/Controllers/AdminController.cs b/backend/WenChuanyi.Api/Controllers/AdminController.cs new file mode 100644 index 0000000..1da310e --- /dev/null +++ b/backend/WenChuanyi.Api/Controllers/AdminController.cs @@ -0,0 +1,92 @@ +using Microsoft.AspNetCore.Mvc; +using WenChuanyi.Api.Dtos; +using WenChuanyi.Api.Models; +using WenChuanyi.Api.Services; + +namespace WenChuanyi.Api.Controllers; + +/// 发送者管理:凭管理码查看文件列表 / 删除文件 +[ApiController] +[Route("api/admin")] +public class AdminController : ControllerBase +{ + private readonly IFreeSql _fsql; + private readonly OssStorageService _oss; + private readonly ILogger _logger; + + public AdminController(IFreeSql fsql, OssStorageService oss, ILogger logger) + { + _fsql = fsql; + _oss = oss; + _logger = logger; + } + + /// 管理码查询文件列表(含上传 IP) + [HttpGet("files/{adminCode}")] + public async Task List(string adminCode) + { + if (string.IsNullOrEmpty(adminCode) || adminCode.Length != 8) + { + return BadRequest(new { message = "管理码无效" }); + } + var list = await _fsql.Select() + .Where(f => f.AdminCode == adminCode) + .OrderByDescending(f => f.CreatedAt) + .ToListAsync(); + return Ok(list.Select(ToDto)); + } + + /// 删除指定文件(body 传 id 或取件码,校验管理码) + [HttpDelete("files/{adminCode}")] + public async Task Delete(string adminCode, [FromBody] DeleteRequest? req) + { + if (string.IsNullOrEmpty(adminCode) || adminCode.Length != 8) + { + return BadRequest(new { message = "管理码无效" }); + } + if (req == null || (req.Id == null && string.IsNullOrEmpty(req.PickCode))) + { + return BadRequest(new { message = "请提供要删除的文件 Id 或取件码" }); + } + + FileItem? item; + if (req.Id != null) + { + item = await _fsql.Select() + .Where(f => f.Id == req.Id.Value && f.AdminCode == adminCode).FirstAsync(); + } + else + { + item = await _fsql.Select() + .Where(f => f.PickCode == req.PickCode && f.AdminCode == adminCode).FirstAsync(); + } + if (item == null) + { + return NotFound(new { message = "文件不存在或管理码不匹配" }); + } + + var deleted = await _fsql.Delete().Where(f => f.Id == item.Id).ExecuteAffrowsAsync() > 0; + if (deleted) + { + _oss.TryDeleteObject(item.ObjectKey); + } + return Ok(new { message = "已删除" }); + } + + private static FileInfoDto ToDto(FileItem f) => new() + { + Id = f.Id, + PickCode = f.PickCode, + FileType = f.FileType, + HasPassword = f.FileType == "private", + OriginalName = f.OriginalName, + Size = f.Size, + MimeType = f.MimeType, + DownloadCount = f.DownloadCount, + UploadIp = f.UploadIp, + IsPermanent = f.IsPermanent, + ExpiresAt = f.ExpiresAt, + CreatedAt = f.CreatedAt, + ExpireText = FileRules.ExpireText(f) + }; +} diff --git a/backend/WenChuanyi.Api/Controllers/FileController.cs b/backend/WenChuanyi.Api/Controllers/FileController.cs new file mode 100644 index 0000000..45b215e --- /dev/null +++ b/backend/WenChuanyi.Api/Controllers/FileController.cs @@ -0,0 +1,392 @@ +using System.Text; +using Aliyun.OSS; +using Microsoft.AspNetCore.Mvc; +using WenChuanyi.Api.Dtos; +using WenChuanyi.Api.Models; +using WenChuanyi.Api.Services; + +namespace WenChuanyi.Api.Controllers; + +/// 文件上传 / 查询 / 标签列表 / 预览 / 下载 +[ApiController] +[Route("api/files")] +public class FileController : ControllerBase +{ + private readonly IFreeSql _fsql; + private readonly CodeGeneratorService _codeGen; + private readonly OssStorageService _oss; + private readonly string _baseUrl; + private readonly ILogger _logger; + + public FileController(IFreeSql fsql, CodeGeneratorService codeGen, OssStorageService oss, + IConfiguration cfg, ILogger logger) + { + _fsql = fsql; + _codeGen = codeGen; + _oss = oss; + _baseUrl = cfg["App:BaseUrl"] ?? ""; + _logger = logger; + } + + // ---------- 上传 ---------- + + /// multipart 上传:file + expireHours(24/168/0) + password + tag(password 与 tag 可同时设置) + [HttpPost("upload")] + [RequestSizeLimit(220_200_960)] // 210MB(Kestrel 同配置) + public async Task Upload([FromForm] int expireHours, [FromForm] string? password, + [FromForm] string? tag, IFormFile? file) + { + if (file == null || file.Length == 0) + { + return BadRequest(new { message = "请选择要上传的文件" }); + } + if (file.Length > FileRules.MaxFileBytes) + { + return BadRequest(new { message = "文件超过 200MB 限制,无法上传" }); + } + + var pwdErr = FileRules.ValidatePassword(password); + if (pwdErr != null) return BadRequest(new { message = pwdErr }); + + tag = string.IsNullOrWhiteSpace(tag) ? null : tag.Trim(); + var tagErr = FileRules.ValidateTag(tag); + if (tagErr != null) return BadRequest(new { message = tagErr }); + + var hasPassword = !string.IsNullOrEmpty(password); + var hasTag = !string.IsNullOrEmpty(tag); + + // 有效期:仅 24/168/0 三档;含标签强制永久 + var effectiveExpire = hasTag ? 0 : (expireHours is 24 or 168 or 0 ? expireHours : 24); + var isPermanent = hasTag || effectiveExpire == 0; + DateTime? expiresAt = isPermanent ? null : DateTime.Now.AddHours(effectiveExpire); + + var fileType = FileRules.ResolveFileType(hasPassword, hasTag); + var originalName = FileRules.SanitizeFileName(file.FileName); + var objectKey = _oss.BuildObjectKey(originalName); + var ip = HttpContext.Connection.RemoteIpAddress?.ToString(); + var contentType = string.IsNullOrEmpty(file.ContentType) ? "application/octet-stream" : file.ContentType; + + try + { + await using var stream = file.OpenReadStream(); + await _oss.UploadAsync(objectKey, stream, contentType); + } + catch (Exception ex) + { + _logger.LogError(ex, "OSS 上传失败:{Name}", file.FileName); + return StatusCode(500, new { message = "文件上传失败,请稍后重试" }); + } + + var item = new FileItem + { + PickCode = await _codeGen.GeneratePickCodeAsync(FileRules.PickCodeLength(fileType)), + AdminCode = await _codeGen.GenerateAdminCodeAsync(), + FileType = fileType, + Password = hasPassword ? password : null, + Tag = hasTag ? tag : null, + OriginalName = originalName, + ObjectKey = objectKey, + Size = file.Length, + MimeType = contentType, + DownloadCount = 0, + UploadIp = ip, + IsPermanent = isPermanent, + ExpiresAt = expiresAt, + CreatedAt = DateTime.Now + }; + await _fsql.Insert(item).ExecuteAffrowsAsync(); + + return Ok(new UploadResultDto + { + Id = item.Id, + PickCode = item.PickCode, + AdminCode = item.AdminCode, + FileType = fileType, + OriginalName = item.OriginalName, + Size = item.Size, + IsPermanent = isPermanent, + ExpiresAt = expiresAt, + PickUrl = BuildPickUrl(item.PickCode) + }); + } + + // ---------- 查询 ---------- + + /// 按取件码查询(私密需带 password;过期返回 410) + [HttpGet("{code}")] + public async Task Query(string code, [FromQuery] string? password) + { + if (!IsPickCode(code)) + { + return NotFound(new { message = "取件码/标签无效" }); + } + var (item, error, status) = await FindAndValidateAsync(code, password); + if (item == null) + { + return StatusCode(status, new { message = error }); + } + return Ok(ToDto(item)); + } + + /// 按标签查询文件列表(CreatedAt 倒序,limit 100) + [HttpGet("by-tag/{tag}")] + public async Task ByTag(string tag) + { + if (string.IsNullOrEmpty(tag) || !tag.All(char.IsLetterOrDigit)) + { + return BadRequest(new { message = "标签无效" }); + } + var list = await _fsql.Select() + .Where(f => f.Tag == tag) + .OrderByDescending(f => f.CreatedAt) + .Limit(100) + .ToListAsync(); + return Ok(list.Select(ToDto)); + } + + // ---------- 下载 ---------- + + /// 流式下载:计数 +1(仅统计),还原原始文件名,支持 Range 断点续传(私密需密码) + [HttpGet("{code}/download")] + public async Task Download(string code, [FromQuery] string? password) + { + var (item, error, status) = await FindAndValidateAsync(code, password); + if (item == null) + { + return StatusCode(status, new { message = error }); + } + await _fsql.Update() + .Set(f => f.DownloadCount + 1) + .Where(f => f.Id == item.Id) + .ExecuteAffrowsAsync(); + await ServeStreamAsync(item, inline: false); + return new EmptyResult(); + } + + // ---------- 预览 ---------- + + /// 在线预览:PDF/图片(≤30MB)/ 音视频白名单(不限大小)inline 流;不计次数 + [HttpGet("{code}/preview")] + public async Task Preview(string code, [FromQuery] string? password) + { + var (item, error, status) = await FindAndValidateAsync(code, password); + if (item == null) + { + return StatusCode(status, new { message = error }); + } + var ext = Path.GetExtension(item.OriginalName).ToLowerInvariant(); + var mime = item.MimeType ?? ""; + if (FileRules.IsText(ext)) + { + return await ServeTextContentAsync(item); + } + if (FileRules.IsPdfOrImage(mime)) + { + if (item.Size > FileRules.MaxPdfImagePreview) + { + return BadRequest(new { message = "文件过大,请下载后查看" }); + } + await ServeStreamAsync(item, inline: true); + return new EmptyResult(); + } + if (FileRules.IsVideoWhitelist(ext) || FileRules.IsAudioWhitelist(ext)) + { + await ServeStreamAsync(item, inline: true); + return new EmptyResult(); + } + return BadRequest(new { message = "该格式不支持在线预览,请下载查看" }); + } + + /// 文本类内容(≤2MB),供在线阅读渲染(私密需密码) + [HttpGet("{code}/content")] + public async Task GetContent(string code, [FromQuery] string? password) + { + var (item, error, status) = await FindAndValidateAsync(code, password); + if (item == null) + { + return StatusCode(status, new { message = error }); + } + if (!FileRules.IsText(Path.GetExtension(item.OriginalName).ToLowerInvariant())) + { + return BadRequest(new { message = "该类型不支持文本预览" }); + } + return await ServeTextContentAsync(item); + } + + // ---------- 私有辅助 ---------- + + private static bool IsPickCode(string code) + => code.Length is 6 or 8 && code.All(char.IsDigit); + + /// 校验取件码 + 过期(懒检查)+ 私密密码;错误时返回 (null, message, status) + private async Task<(FileItem? Item, string? Error, int Status)> FindAndValidateAsync(string code, string? password) + { + if (!IsPickCode(code)) + { + return (null, "取件码/标签无效", StatusCodes.Status404NotFound); + } + var item = await _fsql.Select().Where(f => f.PickCode == code).FirstAsync(); + if (item == null) + { + return (null, "取件码/标签无效", StatusCodes.Status404NotFound); + } + if (!item.IsPermanent && item.ExpiresAt != null && item.ExpiresAt < DateTime.Now) + { + return (null, "文件已过期", StatusCodes.Status410Gone); + } + if (item.FileType == "private") + { + if (string.IsNullOrEmpty(password) || password != item.Password) + { + return (null, "密码错误", StatusCodes.Status401Unauthorized); + } + } + return (item, null, StatusCodes.Status200OK); + } + + private FileInfoDto ToDto(FileItem f) => new() + { + Id = f.Id, + PickCode = f.PickCode, + FileType = f.FileType, + HasPassword = f.FileType == "private", + OriginalName = f.OriginalName, + Size = f.Size, + MimeType = f.MimeType, + DownloadCount = f.DownloadCount, + IsPermanent = f.IsPermanent, + ExpiresAt = f.ExpiresAt, + CreatedAt = f.CreatedAt, + ExpireText = FileRules.ExpireText(f) + }; + + private string BuildPickUrl(string pickCode) + => $"{_baseUrl}/#/pickup?code={pickCode}"; + + /// OSS 流式输出到响应(inline/attachment + Range 206 透传) + private async Task ServeStreamAsync(FileItem item, bool inline) + { + var rangeHeader = Request.Headers.Range.ToString(); + long? start = null, end = null; + if (!string.IsNullOrEmpty(rangeHeader) && TryParseRange(rangeHeader, item.Size, out var rs, out var re)) + { + start = rs; + end = re; + } + OssObject obj; + try + { + obj = _oss.GetObject(item.ObjectKey, start, end); + } + catch (Exception ex) + { + _logger.LogError(ex, "OSS 读取失败:{Key}", item.ObjectKey); + Response.StatusCode = StatusCodes.Status500InternalServerError; + await Response.WriteAsync("文件读取失败,请稍后重试"); + return; + } + + Response.StatusCode = StatusCodes.Status200OK; + Response.ContentType = item.MimeType ?? "application/octet-stream"; + Response.Headers["Accept-Ranges"] = "bytes"; + Response.Headers["Content-Disposition"] = inline + ? "inline" + : $"attachment; filename*=UTF-8''{Uri.EscapeDataString(item.OriginalName)}"; + + if (start.HasValue) + { + Response.StatusCode = StatusCodes.Status206PartialContent; + var actualEnd = end < 0 || end == null ? item.Size - 1 : end.Value; + Response.Headers["Content-Range"] = $"bytes {start}-{actualEnd}/{item.Size}"; + } + Response.Headers["Content-Length"] = obj.ContentLength.ToString(); + await obj.Content.CopyToAsync(Response.Body); + } + + /// 文本内容读取(≤2MB,UTF-8 优先,GBK 兜底) + private async Task ServeTextContentAsync(FileItem item) + { + if (item.Size > FileRules.MaxTextPreview) + { + return BadRequest(new { message = "文件过大,请下载后查看" }); + } + OssObject obj; + try + { + obj = _oss.GetObject(item.ObjectKey); + } + catch (Exception ex) + { + _logger.LogError(ex, "OSS 读取失败:{Key}", item.ObjectKey); + return StatusCode(500, new { message = "文件读取失败,请稍后重试" }); + } + using var ms = new MemoryStream(); + await obj.Content.CopyToAsync(ms); + var bytes = ms.ToArray(); + var text = DecodeText(bytes); + return Content(text, "text/plain; charset=utf-8"); + } + + private static string DecodeText(byte[] bytes) + { + if (bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) + { + return Encoding.UTF8.GetString(bytes, 3, bytes.Length - 3); + } + if (bytes.Length >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) + { + return Encoding.Unicode.GetString(bytes, 2, bytes.Length - 2); + } + return IsValidUtf8(bytes) ? Encoding.UTF8.GetString(bytes) : Encoding.GetEncoding("GBK").GetString(bytes); + } + + private static bool IsValidUtf8(byte[] bytes) + { + try + { + var decoder = new UTF8Encoding(false, true).GetDecoder(); + var charCount = decoder.GetCharCount(bytes, 0, bytes.Length, flush: true); + return charCount >= 0; + } + catch (DecoderFallbackException) + { + return false; + } + } + + private static bool TryParseRange(string rangeHeader, long total, out long start, out long end) + { + start = 0; + end = -1; // -1 表示到文件末尾 + if (string.IsNullOrEmpty(rangeHeader) || !rangeHeader.StartsWith("bytes=", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + var spec = rangeHeader["bytes=".Length..]; + var idx = spec.IndexOf('-'); + if (idx < 0) return false; + var startPart = spec[..idx]; + var endPart = spec[(idx + 1)..]; + if (startPart.Length > 0 && long.TryParse(startPart, out start)) + { + if (start >= total) return false; + if (endPart.Length > 0 && long.TryParse(endPart, out var e)) + { + end = Math.Min(e, total - 1); + if (start > end) return false; + } + // "bytes=start-":end 保持 -1(到末尾) + } + else if (endPart.Length > 0 && long.TryParse(endPart, out var suffix)) + { + // "bytes=-N":末尾 N 字节 + start = Math.Max(0, total - suffix); + end = -1; + } + else + { + return false; + } + return true; + } +} diff --git a/backend/WenChuanyi.Api/Controllers/OpenApiController.cs b/backend/WenChuanyi.Api/Controllers/OpenApiController.cs new file mode 100644 index 0000000..e944064 --- /dev/null +++ b/backend/WenChuanyi.Api/Controllers/OpenApiController.cs @@ -0,0 +1,156 @@ +using Microsoft.AspNetCore.Mvc; +using WenChuanyi.Api.Dtos; +using WenChuanyi.Api.Models; +using WenChuanyi.Api.Services; + +namespace WenChuanyi.Api.Controllers; + +/// 开放 API:按服务端文件路径上传(共享/私密/标签)+ 下载链接查询 +[ApiController] +[Route("api/open")] +public class OpenApiController : ControllerBase +{ + private readonly IFreeSql _fsql; + private readonly CodeGeneratorService _codeGen; + private readonly OssStorageService _oss; + private readonly string _baseUrl; + private readonly string _uploadRoot; + private readonly ILogger _logger; + + public OpenApiController(IFreeSql fsql, CodeGeneratorService codeGen, OssStorageService oss, + IConfiguration cfg, ILogger logger) + { + _fsql = fsql; + _codeGen = codeGen; + _oss = oss; + _baseUrl = cfg["App:BaseUrl"] ?? ""; + _uploadRoot = Path.GetFullPath(cfg["OpenApi:UploadRoot"] ?? throw new InvalidOperationException("缺少配置 OpenApi:UploadRoot")); + _logger = logger; + } + + /// 公开上传-共享:expiresHours(0=永久,缺省 24);pwd/tag 可同时提供(不互斥) + [HttpPost("upload-public")] + public Task UploadPublic([FromBody] OpenUploadRequest req) + => UploadFromPathAsync(req.FilePath, req.ExpiresHours, req.Pwd, req.Tag); + + /// 公开上传-私密:必填密码,可同时带标签 + [HttpPost("upload-price")] + public Task UploadPrice([FromBody] OpenUploadPriceRequest req) + => UploadFromPathAsync(req.FilePath, null, req.Pwd, req.Tag); + + /// 公开上传-标签:必填标签(永久保存),可同时带密码 + [HttpPost("upload-tag")] + public Task UploadTag([FromBody] OpenUploadTagRequest req) + => UploadFromPathAsync(req.FilePath, null, req.Pwd, req.Tag); + + /// 公开下载链接查询:共享/标签返回直接下载 URL,私密跳取件页手动输密码 + [HttpGet("download/{pickCode}")] + public async Task Download(string pickCode) + { + if (pickCode.Length is not (6 or 8) || !pickCode.All(char.IsDigit)) + { + return NotFound(new { message = "取件码无效" }); + } + var item = await _fsql.Select().Where(f => f.PickCode == pickCode).FirstAsync(); + if (item == null) + { + return NotFound(new { message = "取件码无效" }); + } + if (!item.IsPermanent && item.ExpiresAt != null && item.ExpiresAt < DateTime.Now) + { + return StatusCode(410, new { message = "文件已过期" }); + } + var needPassword = item.FileType == "private"; + return Ok(new OpenDownloadResult + { + PickUrl = BuildPickUrl(pickCode), + DownloadUrl = needPassword ? BuildPickUrl(pickCode) : BuildDownloadUrl(pickCode), + NeedPassword = needPassword + }); + } + + // ---------- 私有 ---------- + + private async Task UploadFromPathAsync(string filePath, long? expiresHours, string? pwd, string? tag) + { + var fullPath = ResolveSafePath(filePath); + if (fullPath == null) + { + return BadRequest(new { message = "文件路径不在白名单目录内" }); + } + if (!System.IO.File.Exists(fullPath)) + { + return NotFound(new { message = "文件不存在" }); + } + var fi = new FileInfo(fullPath); + if (fi.Length > FileRules.MaxFileBytes) + { + return BadRequest(new { message = "文件超过 200MB 限制" }); + } + + var pwdErr = FileRules.ValidatePassword(pwd); + if (pwdErr != null) return BadRequest(new { message = pwdErr }); + + tag = string.IsNullOrWhiteSpace(tag) ? null : tag.Trim(); + var tagErr = FileRules.ValidateTag(tag); + if (tagErr != null) return BadRequest(new { message = tagErr }); + + var hasPassword = !string.IsNullOrEmpty(pwd); + var hasTag = !string.IsNullOrEmpty(tag); + var isPermanent = hasTag || expiresHours == 0; + DateTime? expiresAt = isPermanent ? null : DateTime.Now.AddHours(expiresHours ?? 24); + + var fileType = FileRules.ResolveFileType(hasPassword, hasTag); + var objectKey = _oss.BuildObjectKey(fi.Name); + var ip = HttpContext.Connection.RemoteIpAddress?.ToString(); + + try + { + await using var stream = System.IO.File.OpenRead(fullPath); + await _oss.UploadAsync(objectKey, stream, "application/octet-stream"); + } + catch (Exception ex) + { + _logger.LogError(ex, "公开上传 OSS 失败:{Path}", fullPath); + return StatusCode(500, new { message = "文件上传失败" }); + } + + var item = new FileItem + { + PickCode = await _codeGen.GeneratePickCodeAsync(FileRules.PickCodeLength(fileType)), + AdminCode = await _codeGen.GenerateAdminCodeAsync(), + FileType = fileType, + Password = hasPassword ? pwd : null, + Tag = hasTag ? tag : null, + OriginalName = fi.Name, + ObjectKey = objectKey, + Size = fi.Length, + MimeType = "application/octet-stream", + DownloadCount = 0, + UploadIp = ip, + IsPermanent = isPermanent, + ExpiresAt = expiresAt, + CreatedAt = DateTime.Now + }; + await _fsql.Insert(item).ExecuteAffrowsAsync(); + + return Ok(new OpenResult + { + PickCode = item.PickCode, + PickUrl = BuildPickUrl(item.PickCode), + DownloadUrl = fileType == "private" ? BuildPickUrl(item.PickCode) : BuildDownloadUrl(item.PickCode) + }); + } + + /// 路径规范化 + 白名单前缀校验,防路径穿越 + private string? ResolveSafePath(string filePath) + { + if (string.IsNullOrWhiteSpace(filePath)) return null; + var full = Path.GetFullPath(filePath); + if (!full.StartsWith(_uploadRoot, StringComparison.OrdinalIgnoreCase)) return null; + return full; + } + + private string BuildPickUrl(string pickCode) => $"{_baseUrl}/#/pickup?code={pickCode}"; + private string BuildDownloadUrl(string pickCode) => $"{_baseUrl}/api/files/{pickCode}/download"; +} diff --git a/backend/WenChuanyi.Api/Dtos/FileInfoDto.cs b/backend/WenChuanyi.Api/Dtos/FileInfoDto.cs new file mode 100644 index 0000000..49ba9eb --- /dev/null +++ b/backend/WenChuanyi.Api/Dtos/FileInfoDto.cs @@ -0,0 +1,22 @@ +namespace WenChuanyi.Api.Dtos; + +/// 文件信息 / 管理列表 / 标签列表 DTO(不暴露密码明文) +public class FileInfoDto +{ + public long Id { get; set; } + public string PickCode { get; set; } = null!; + public string FileType { get; set; } = null!; + public bool HasPassword { get; set; } + public string OriginalName { get; set; } = null!; + public long Size { get; set; } + public string? MimeType { get; set; } + public int DownloadCount { get; set; } + /// 上传者 IP(安全审计):仅管理列表填充,取件/标签/公开接口不暴露 + public string? UploadIp { get; set; } + public bool IsPermanent { get; set; } + public DateTime? ExpiresAt { get; set; } + public DateTime CreatedAt { get; set; } + + /// 剩余有效期文本(永久/已过期/N天N小时) + public string ExpireText { get; set; } = "永久"; +} diff --git a/backend/WenChuanyi.Api/Dtos/OpenApiDtos.cs b/backend/WenChuanyi.Api/Dtos/OpenApiDtos.cs new file mode 100644 index 0000000..5ac10dd --- /dev/null +++ b/backend/WenChuanyi.Api/Dtos/OpenApiDtos.cs @@ -0,0 +1,49 @@ +namespace WenChuanyi.Api.Dtos; + +/// 公开上传-共享文件:有效期小时数(0=永久,缺省 24),pwd/tag 可同时提供(不互斥) +public class OpenUploadRequest +{ + public string FilePath { get; set; } = null!; + public long ExpiresHours { get; set; } = 24; + public string? Pwd { get; set; } + public string? Tag { get; set; } +} + +/// 公开上传-私密文件:必须有密码,可同时带标签 +public class OpenUploadPriceRequest +{ + public string FilePath { get; set; } = null!; + public string Pwd { get; set; } = null!; + public string? Tag { get; set; } +} + +/// 公开上传-标签文件:必须带标签(永久保存),可同时带密码 +public class OpenUploadTagRequest +{ + public string FilePath { get; set; } = null!; + public string Tag { get; set; } = null!; + public string? Pwd { get; set; } +} + +/// 公开上传响应 +public class OpenResult +{ + public string PickCode { get; set; } = null!; + public string PickUrl { get; set; } = null!; + public string DownloadUrl { get; set; } = null!; +} + +/// 公开下载链接查询响应 +public class OpenDownloadResult +{ + public string DownloadUrl { get; set; } = null!; + public string PickUrl { get; set; } = null!; + public bool NeedPassword { get; set; } +} + +/// 管理删除请求体:按 Id 或取件码删除 +public class DeleteRequest +{ + public long? Id { get; set; } + public string? PickCode { get; set; } +} diff --git a/backend/WenChuanyi.Api/Dtos/UploadResultDto.cs b/backend/WenChuanyi.Api/Dtos/UploadResultDto.cs new file mode 100644 index 0000000..96954df --- /dev/null +++ b/backend/WenChuanyi.Api/Dtos/UploadResultDto.cs @@ -0,0 +1,17 @@ +namespace WenChuanyi.Api.Dtos; + +/// 上传响应:取件凭证 + 管理码 + 文件信息 +public class UploadResultDto +{ + public long Id { get; set; } + public string PickCode { get; set; } = null!; + public string AdminCode { get; set; } = null!; + public string FileType { get; set; } = null!; + public string OriginalName { get; set; } = null!; + public long Size { get; set; } + public bool IsPermanent { get; set; } + public DateTime? ExpiresAt { get; set; } + + /// 取件页链接(二维码内容) + public string PickUrl { get; set; } = null!; +} diff --git a/backend/WenChuanyi.Api/Models/FileItem.cs b/backend/WenChuanyi.Api/Models/FileItem.cs new file mode 100644 index 0000000..928e377 --- /dev/null +++ b/backend/WenChuanyi.Api/Models/FileItem.cs @@ -0,0 +1,63 @@ +using FreeSql.DataAnnotations; + +namespace WenChuanyi.Api.Models; + +/// 文件记录(files 表,FreeSql CodeFirst 自动建表) +[Table(Name = "files")] +[Index("uk_pickcode", nameof(PickCode), true)] +[Index("uk_admincode", nameof(AdminCode), true)] +[Index("idx_tag", nameof(Tag))] +public class FileItem +{ + [Column(IsPrimary = true, IsIdentity = true)] + public long Id { get; set; } + + /// 取件码(共享 8 位 / 私密 6 位),唯一索引 + [Column(StringLength = 8)] + public string PickCode { get; set; } = null!; + + /// 管理码 8 位字母数字,唯一索引 + [Column(StringLength = 8)] + public string AdminCode { get; set; } = null!; + + /// standard(无密码无标签)/ private(有密码)/ tagged(仅标签) + [Column(StringLength = 10)] + public string FileType { get; set; } = null!; + + /// 私密文件 4-12 位密码(可同时设置标签,不互斥) + [Column(StringLength = 12)] + public string? Password { get; set; } + + /// 标签(仅英文+数字),普通索引(非唯一,一对多),可与密码同时设置 + [Column(StringLength = 32)] + public string? Tag { get; set; } + + /// 原始文件名(下载展示) + [Column(StringLength = 255)] + public string OriginalName { get; set; } = null!; + + /// OSS 对象键:文传易2026/{yyyyMM}/{Guid}{ext} + [Column(StringLength = 255)] + public string ObjectKey { get; set; } = null!; + + /// 字节数 + public long Size { get; set; } + + [Column(StringLength = 100)] + public string? MimeType { get; set; } + + /// 下载次数(仅统计、不限制) + public int DownloadCount { get; set; } + + /// 上传者 IP(IPv4/IPv6,安全审计,仅管理列表展示) + [Column(StringLength = 45)] + public string? UploadIp { get; set; } + + /// 含标签(无论是否设密码)强制 true(永久保存,不自动过期) + public bool IsPermanent { get; set; } + + /// 过期时间(IsPermanent=true 时为 null) + public DateTime? ExpiresAt { get; set; } + + public DateTime CreatedAt { get; set; } +} diff --git a/backend/WenChuanyi.Api/Program.cs b/backend/WenChuanyi.Api/Program.cs new file mode 100644 index 0000000..b892e28 --- /dev/null +++ b/backend/WenChuanyi.Api/Program.cs @@ -0,0 +1,49 @@ +using System.Text; +using FreeSql; +using WenChuanyi.Api.Models; +using WenChuanyi.Api.Services; + +Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); + +// CORS:开发期前后端分离宽松策略;生产同目录同源不受影响 +builder.Services.AddCors(o => o.AddPolicy("any", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod())); + +// FreeSql + CodeFirst 自动建表 +var fsql = new FreeSqlBuilder() + .UseConnectionString(DataType.MySql, builder.Configuration.GetConnectionString("MySql")!) + .UseAutoSyncStructure(true) + .UseNoneCommandParameter(true) + .Build(); + +try +{ + fsql.CodeFirst.SyncStructure(); +} +catch (Exception ex) +{ + Console.WriteLine("[文传易] 数据库初始化失败:" + ex.Message); + Console.WriteLine("[文传易] 请检查 appsettings.json 的 ConnectionStrings:MySql"); + Console.WriteLine("[文传易] (服务器 116.198.221.125:3306,账号 wenchuanyi 需具备建表权限)"); + throw; +} + +builder.Services.AddSingleton(fsql); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(); + +var app = builder.Build(); + +app.UseCors("any"); + +app.UseDefaultFiles(); +app.UseStaticFiles(); + +app.MapControllers(); +app.MapFallbackToFile("index.html"); + +app.Run(); diff --git a/backend/WenChuanyi.Api/Properties/launchSettings.json b/backend/WenChuanyi.Api/Properties/launchSettings.json new file mode 100644 index 0000000..79e3e2e --- /dev/null +++ b/backend/WenChuanyi.Api/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:6294", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "http://localhost:5039", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/backend/WenChuanyi.Api/Services/CodeGeneratorService.cs b/backend/WenChuanyi.Api/Services/CodeGeneratorService.cs new file mode 100644 index 0000000..2eefb0d --- /dev/null +++ b/backend/WenChuanyi.Api/Services/CodeGeneratorService.cs @@ -0,0 +1,57 @@ +using WenChuanyi.Api.Models; + +namespace WenChuanyi.Api.Services; + +/// 取件码/管理码生成与查重(冲突自动重生成) +public class CodeGeneratorService +{ + // 排除易混淆 0/O/1/I/l + private const string AdminChars = "ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789"; + + private readonly IFreeSql _fsql; + + public CodeGeneratorService(IFreeSql fsql) => _fsql = fsql; + + /// 生成 length 位纯数字取件码(全局唯一) + public async Task GeneratePickCodeAsync(int length) + { + for (var i = 0; i < 30; i++) + { + var code = GenerateDigits(length); + var exists = await _fsql.Select().Where(f => f.PickCode == code).AnyAsync(); + if (!exists) + { + return code; + } + } + throw new InvalidOperationException("取件码生成冲突次数过多,请重试"); + } + + /// 生成 8 位字母数字管理码(全局唯一) + public async Task GenerateAdminCodeAsync() + { + for (var i = 0; i < 30; i++) + { + var code = new string(Enumerable.Range(0, 8) + .Select(_ => AdminChars[Random.Shared.Next(AdminChars.Length)]).ToArray()); + var exists = await _fsql.Select().Where(f => f.AdminCode == code).AnyAsync(); + if (!exists) + { + return code; + } + } + throw new InvalidOperationException("管理码生成冲突次数过多,请重试"); + } + + private static string GenerateDigits(int length) + { + var first = Random.Shared.Next(1, 10); // 首位非 0 + var sb = new System.Text.StringBuilder(); + sb.Append(first); + for (var i = 1; i < length; i++) + { + sb.Append(Random.Shared.Next(0, 10)); + } + return sb.ToString(); + } +} diff --git a/backend/WenChuanyi.Api/Services/ExpiredFileCleanerService.cs b/backend/WenChuanyi.Api/Services/ExpiredFileCleanerService.cs new file mode 100644 index 0000000..5040d13 --- /dev/null +++ b/backend/WenChuanyi.Api/Services/ExpiredFileCleanerService.cs @@ -0,0 +1,67 @@ +using WenChuanyi.Api.Models; + +namespace WenChuanyi.Api.Services; + +/// 过期文件定时清理:每 N 分钟扫描 IsPermanent=false 且已过期的记录,先删记录再删 OSS 对象 +public class ExpiredFileCleanerService : BackgroundService +{ + private readonly IServiceScopeFactory _scopeFactory; + private readonly ILogger _logger; + private readonly TimeSpan _interval; + + public ExpiredFileCleanerService( + IServiceScopeFactory scopeFactory, + IConfiguration cfg, + ILogger logger) + { + _scopeFactory = scopeFactory; + _logger = logger; + var minutes = cfg.GetValue("Cleaner:IntervalMinutes", 30); + _interval = TimeSpan.FromMinutes(Math.Max(1, minutes)); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("过期文件清理任务启动,间隔 {Minutes} 分钟", _interval.TotalMinutes); + while (!stoppingToken.IsCancellationRequested) + { + try + { + await CleanExpiredAsync(stoppingToken); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "过期文件清理任务异常"); + } + await Task.Delay(_interval, stoppingToken); + } + } + + private async Task CleanExpiredAsync(CancellationToken ct) + { + await using var scope = _scopeFactory.CreateAsyncScope(); + var fsql = scope.ServiceProvider.GetRequiredService(); + var oss = scope.ServiceProvider.GetRequiredService(); + + var expired = await fsql.Select() + .Where(f => !f.IsPermanent && f.ExpiresAt != null && f.ExpiresAt < DateTime.Now) + .ToListAsync(ct); + + if (expired.Count == 0) + { + return; + } + + _logger.LogInformation("发现 {Count} 个过期文件,开始清理", expired.Count); + foreach (var item in expired) + { + // 先删数据库记录 + var deleted = await fsql.Delete().Where(f => f.Id == item.Id).ExecuteAffrowsAsync(ct) > 0; + // 再删 OSS 对象(失败仅记日志,不阻塞任务) + if (deleted) + { + oss.TryDeleteObject(item.ObjectKey); + } + } + } +} diff --git a/backend/WenChuanyi.Api/Services/FileRules.cs b/backend/WenChuanyi.Api/Services/FileRules.cs new file mode 100644 index 0000000..31f9ec2 --- /dev/null +++ b/backend/WenChuanyi.Api/Services/FileRules.cs @@ -0,0 +1,108 @@ +using System.Text.RegularExpressions; +using WenChuanyi.Api.Models; + +namespace WenChuanyi.Api.Services; + +/// 业务规则共享静态工具:密码/标签校验、模式判定、预览分类 +public static class FileRules +{ + public const long MaxFileBytes = 200L * 1024 * 1024; // 200MB + public const long MaxTextPreview = 2L * 1024 * 1024; // 文本预览 2MB + public const long MaxPdfImagePreview = 30L * 1024 * 1024; // PDF/图片预览 30MB + + /// 清洗文件名:去除 Windows 非法字符/控制字符、首尾空格与点、保留名与超长限制,防止存储路径注入 + public static string SanitizeFileName(string? name) + { + if (string.IsNullOrWhiteSpace(name)) return "未命名"; + var dot = name.LastIndexOf('.'); + var ext = dot >= 0 ? name[(dot + 1)..] : ""; + var baseName = dot >= 0 ? name[..dot] : name; + + var cleanBase = new string(baseName + .Where(c => c >= 32 && c != 127 && c != '<' && c != '>' && c != ':' && c != '"' + && c != '/' && c != '\\' && c != '|' && c != '?' && c != '*') + .ToArray()).Trim().TrimEnd('.', ' '); + + var result = string.IsNullOrEmpty(cleanBase) ? "未命名" : cleanBase; + + // Windows 保留设备名:CON PRN AUX NUL COM1-9 LPT1-9(含扩展名也禁止) + var upperBase = result.Split('.')[0].ToUpperInvariant(); + if (upperBase is "CON" or "PRN" or "AUX" or "NUL" + || Regex.IsMatch(upperBase, @"^COM[1-9]$") || Regex.IsMatch(upperBase, @"^LPT[1-9]$")) + { + result = "_" + result; + } + + // 长度限制(保留扩展名,主体最长 180 字符) + if (result.Length > 180) result = result[..180]; + + // 扩展名仅保留字母/数字/点,最长 10 字符 + var extRaw = new string(ext.Where(c => char.IsLetterOrDigit(c) || c == '.').ToArray()).TrimStart('.'); + var cleanExt = extRaw.Length > 0 ? "." + (extRaw.Length > 10 ? extRaw[..10] : extRaw) : ""; + + return result + cleanExt; + } + + /// 校验密码(4-12 位),null 表示通过 + public static string? ValidatePassword(string? pwd) + { + if (string.IsNullOrEmpty(pwd)) return null; + if (pwd.Length < 4 || pwd.Length > 12) + { + return "密码长度须为 4-12 位"; + } + return null; + } + + /// 校验标签:仅英文+数字;纯数字须 >8 位;含英文须 >4 位;null 表示通过 + public static string? ValidateTag(string? tag) + { + if (string.IsNullOrEmpty(tag)) return null; + if (!Regex.IsMatch(tag, "^[A-Za-z0-9]+$")) + { + return "标签仅允许英文与数字,不能包含符号或空格"; + } + if (Regex.IsMatch(tag, "^[0-9]+$")) + { + if (tag.Length <= 8) + { + return "纯数字标签须超过 8 位(建议使用 11 位手机号码)"; + } + } + else if (tag.Length <= 4) + { + return "含英文字母的标签须超过 4 位"; + } + return null; + } + + /// 模式判定:有密码→private;仅标签→tagged;否则 standard + public static string ResolveFileType(bool hasPassword, bool hasTag) + => hasPassword ? "private" : hasTag ? "tagged" : "standard"; + + /// 取件码位数:私密 6 位,其余 8 位 + public static int PickCodeLength(string fileType) => fileType == "private" ? 6 : 8; + + /// 剩余有效期文本 + public static string ExpireText(FileItem f) + { + if (f.IsPermanent || f.ExpiresAt == null) return "永久"; + var remain = f.ExpiresAt.Value - DateTime.Now; + if (remain <= TimeSpan.Zero) return "已过期"; + if (remain.TotalDays >= 1) return $"{(int)remain.TotalDays} 天"; + if (remain.TotalHours >= 1) return $"{(int)remain.TotalHours} 小时"; + return $"{(int)Math.Max(1, remain.TotalMinutes)} 分钟"; + } + + public static bool IsText(string ext) + => ext is ".txt" or ".md" or ".xml" or ".json" or ".csv" or ".log" or ".ini" or ".conf" + or ".srt" or ".html" or ".htm" or ".css" or ".js" or ".ts" or ".cs" or ".java" or ".sql" + or ".yml" or ".yaml" or ".sh" or ".bat" or ".ps1" or ".py" or ".go" or ".c" or ".h" or ".php"; + + public static bool IsPdfOrImage(string mime) + => mime is "application/pdf" + or "image/png" or "image/jpeg" or "image/gif" or "image/webp" or "image/bmp" or "image/svg+xml"; + + public static bool IsVideoWhitelist(string ext) => ext is ".mp4" or ".webm"; + public static bool IsAudioWhitelist(string ext) => ext is ".mp3" or ".wav" or ".m4a" or ".aac" or ".ogg"; +} diff --git a/backend/WenChuanyi.Api/Services/OssStorageService.cs b/backend/WenChuanyi.Api/Services/OssStorageService.cs new file mode 100644 index 0000000..dfcb9f8 --- /dev/null +++ b/backend/WenChuanyi.Api/Services/OssStorageService.cs @@ -0,0 +1,68 @@ +using Aliyun.OSS; + +namespace WenChuanyi.Api.Services; + +/// OSS 存储服务:对象键构造、流式上传/下载/删除(后端中转,前端不接触凭据) +public class OssStorageService +{ + private readonly OssClient _client; + private readonly string _bucket; + private readonly string _baseKey; + private readonly ILogger _logger; + + public OssStorageService(IConfiguration cfg, ILogger logger) + { + var endpoint = cfg["Oss:Endpoint"] ?? throw new InvalidOperationException("缺少配置 Oss:Endpoint"); + var ak = cfg["Oss:AccessKeyId"] ?? throw new InvalidOperationException("缺少配置 Oss:AccessKeyId"); + var sk = cfg["Oss:AccessKeySecret"] ?? throw new InvalidOperationException("缺少配置 Oss:AccessKeySecret"); + _bucket = cfg["Oss:Bucket"] ?? throw new InvalidOperationException("缺少配置 Oss:Bucket"); + _baseKey = cfg["Oss:BaseKey"] ?? "文传易2026"; + _client = new OssClient(endpoint, ak, sk); + _logger = logger; + } + + /// 构造对象键:文传易2026/{yyyyMM}/{Guid}{ext}(按上传月份建子目录) + public string BuildObjectKey(string originalName) + { + var month = DateTime.Now.ToString("yyyyMM"); + var ext = Path.GetExtension(originalName).ToLowerInvariant(); + return $"{_baseKey}/{month}/{Guid.NewGuid():N}{ext}"; + } + + /// 流式上传(不落本地、不整体读内存)。ContentType 会去除参数部分(如 ";charset=utf-8"), + /// 因为 OSS SDK 对带参数的 Content-Type 签名会与服务端校验不一致,导致 403 签名不匹配。 + public async Task UploadAsync(string objectKey, Stream stream, string contentType) + { + var safeContentType = (contentType ?? "application/octet-stream").Split(';')[0].Trim(); + if (string.IsNullOrEmpty(safeContentType)) safeContentType = "application/octet-stream"; + var req = new PutObjectRequest(_bucket, objectKey, stream); + req.Metadata = new ObjectMetadata { ContentType = safeContentType }; + await Task.Run(() => _client.PutObject(req)).ConfigureAwait(false); + } + + /// 获取对象(支持 Range;end 为 -1 表示到文件末尾) + public OssObject GetObject(string objectKey, long? start = null, long? end = null) + { + var req = new GetObjectRequest(_bucket, objectKey); + if (start.HasValue || end.HasValue) + { + req.SetRange(start ?? 0, end ?? -1); + } + return _client.GetObject(req); + } + + /// 删除对象(失败不抛出,由调用方记录日志) + public bool TryDeleteObject(string objectKey) + { + try + { + _client.DeleteObject(_bucket, objectKey); + return true; + } + catch (Exception ex) + { + _logger.LogError(ex, "OSS 对象删除失败:{ObjectKey}", objectKey); + return false; + } + } +} diff --git a/backend/WenChuanyi.Api/WenChuanyi.Api.csproj b/backend/WenChuanyi.Api/WenChuanyi.Api.csproj new file mode 100644 index 0000000..77125d7 --- /dev/null +++ b/backend/WenChuanyi.Api/WenChuanyi.Api.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/backend/WenChuanyi.Api/WenChuanyi.Api.http b/backend/WenChuanyi.Api/WenChuanyi.Api.http new file mode 100644 index 0000000..655a7d6 --- /dev/null +++ b/backend/WenChuanyi.Api/WenChuanyi.Api.http @@ -0,0 +1,6 @@ +@WenChuanyi.Api_HostAddress = http://localhost:5039 + +GET {{WenChuanyi.Api_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/backend/WenChuanyi.Api/appsettings.example.json b/backend/WenChuanyi.Api/appsettings.example.json new file mode 100644 index 0000000..51f90bb --- /dev/null +++ b/backend/WenChuanyi.Api/appsettings.example.json @@ -0,0 +1,39 @@ +{ + "App": { + "BaseUrl": "https://wenchuanyi.bbitcn.net" + }, + "ConnectionStrings": { + "MySql": "Data Source=116.198.221.125;Port=3306;User ID=wenchuanyi;Password=YOUR_DB_PASSWORD;Initial Catalog=wenchuanyi;Charset=utf8mb4;SslMode=None;Allow User Variables=true;Treat Tiny As Boolean=true" + }, + "Oss": { + "Endpoint": "oss-cn-chengdu.aliyuncs.com", + "STSEndpoint": "sts.cn-chengdu.aliyuncs.com", + "Bucket": "bbit-f8-web", + "AccessKeyId": "YOUR_ACCESS_KEY_ID", + "AccessKeySecret": "YOUR_ACCESS_KEY_SECRET", + "BaseKey": "文传易2026" + }, + "OpenApi": { + "UploadRoot": "D:\\WenChuanyi\\UploadRoot" + }, + "Cleaner": { + "IntervalMinutes": 30 + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:5280" + } + }, + "Limits": { + "MaxRequestBodySize": 220200960 + } + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/deploy/IIS部署与FTP发布.md b/deploy/IIS部署与FTP发布.md new file mode 100644 index 0000000..2dec030 --- /dev/null +++ b/deploy/IIS部署与FTP发布.md @@ -0,0 +1,163 @@ +# 文传易 · IIS 部署与 FTP 发布手册 + +> 目标环境:Windows Server + IIS 10 + .NET 8 Hosting Bundle +> 站点地址:`https://wenchuanyi.bbitcn.net` +> 发布方式:FTP(`ftp://116.198.221.125`,默认端口 21,用户 `wenchuanyi`) + +> **当前状态(2026-08-23)**:本地全流程已通过冒烟测试—— +> 上传(共享/私密/标签,含 200MB 拦截)→ 二维码与取件码 → 取件查询/下载(计数)→ 标签列表 → 管理列表(含上传 IP)/删除,均已验证;数据库表已建、OSS 真实凭据已写入 `appsettings.json`(该文件不入库)。服务器上首次发布后,建议用手机微信扫码访问正式站再做一轮真机验证(含微信聊天记录选文件)。 + +--- + +## 1. 部署架构 + +``` +浏览器 + │ https://wenchuanyi.bbitcn.net + ▼ +IIS 站点(物理路径 = 站点根目录) + ├── index.html / assets/* ← 前端 dist 构建产物 + ├── web.config ← 由 dotnet publish 自动生成(ANCM InProcess) + ├── WenChuanyi.Api.dll ← 后端发布输出 + └── appsettings.json ← 数据库连接串 / OSS 凭据 / 上传限制 +``` + +- 前端静态资源与后端发布输出**放在同一 IIS 站点物理路径**(默认文件夹或 `wwwroot`)。 +- 后端 `UseStaticFiles` 提供前端静态文件,`MapFallbackToFile("index.html")` 兜底 SPA 路由; + `/api` 与 `/api/open` 由 ASP.NET Core Module(ANCM)直接处理。 +- 站点根目录布局见上;后端发布产物全部文件与前端 `dist/` 内容合并进同一目录。 + +--- + +## 2. 服务器一次性准备(首次部署前) + +1. **安装 .NET 8 Hosting Bundle** + - 下载:https://dotnet.microsoft.com/download/dotnet/8.0(选择 **Hosting Bundle**) + - 安装后 IIS 中会出现 **ASP.NET Core Module v2**。 +2. **创建 IIS 站点** + - IIS → 右键「网站」→ 添加网站; + - 站点名称:`wenchuanyi`; + - 物理路径:`D:\wenchuanyi`(或任意磁盘,站点根目录); + - 端口 80(HTTP)→ 后续绑定 443 + 证书,域名 `wenchuanyi.bbitcn.net`; + - 应用程序池:.NET CLR 版本选「**无托管代码**」(InProcess 托管由 ANCM 处理)。 +3. **HTTPS 证书**:为 `wenchuanyi.bbitcn.net` 绑定 SSL 证书(企业已有证书或申请免费证书)。 +4. **防火墙**:放行 80 / 443(及测试期 5280)。 + +> 排障:若站点 502.5 / 500.30,临时开启 web.config 中的 `stdoutLogEnabled="true"` 查看 `stdoutLog` 输出。 + +--- + +## 3. 构建发布包(开发机执行) + +### 3.1 后端 + +```powershell +cd backend/WenChuanyi.Api +dotnet publish -c Release +``` + +- 输出目录:`bin/Release/net8.0/publish/` +- Framework-dependent 模式,web.config 自动生成(`hostingModel="inprocess"`)。 +- 发布前确认 `appsettings.json` 已配置真实凭据(见第 4 节)。 + +### 3.2 前端 + +```powershell +cd frontend +npm install # 首次 +npm run build +``` + +- 输出目录:`frontend/dist/`(index.html + assets/*)。 +- 构建目标 ES2018,兼容微信 X5 内核。 + +--- + +## 4. 敏感配置(appsettings.json) + +`backend/WenChuanyi.Api/appsettings.json` 中需填写真实值,**该文件已在 .gitignore 中,不会入库**: + +| 配置节 | 键 | 说明 | +| --- | --- | --- | +| `ConnectionStrings:MySql` | Password | 数据库密码(`r7P^f*v7rFts`) | +| `Oss` | AccessKeyId | 阿里云 OSS AK | +| `Oss` | AccessKeySecret | 阿里云 OSS SK | +| `OpenApi` | UploadRoot | 公开 API 可读取的服务端目录白名单 | +| `App` | BaseUrl | 站点地址,用于生成取件页链接(默认 `https://wenchuanyi.bbitcn.net`) | + +- 连接串密码含 `^`、`*`,不含 `;` / `=`,无需特殊转义,JSON 原样写入即可。 +- 模板参考:`appsettings.example.json`(占位符 `YOUR_DB_PASSWORD` / `YOUR_ACCESS_KEY_ID` / `YOUR_ACCESS_KEY_SECRET`)。 + +--- + +## 5. FTP 发布 + +### 5.1 手动发布 + +用任意 FTP 客户端(FileZilla / WinSCP 等,**被动模式**)登录: + +- 主机:`116.198.221.125` +- 端口:`21` +- 用户:`wenchuanyi` +- 密码:同数据库密码 + +上传清单(目标 = IIS 站点根目录,如 `D:\wenchuanyi`): + +``` +前端 dist/ 全部文件 → 站点根目录 +后端 bin/Release/net8.0/publish/* → 站点根目录(覆盖) +``` + +> 注意:web.config、appsettings.json 等必须位于站点根目录,勿放入子文件夹。 + +### 5.2 一键脚本发布 + +已提供 `deploy/publish.ps1`,一条命令完成「后端 publish + 前端 build + FTP 上传」: + +```powershell +powershell -ExecutionPolicy Bypass -File deploy/publish.ps1 +``` + +### 5.3 发布后生效 + +- FTP 上传完成后,**回收应用程序池**使新版本生效: + - IIS 管理器 → 应用程序池 → `wenchuanyi` → 右键「回收」; + - 或命令行:`C:\Windows\System32\inetsrv\appcmd recycle apppool /apppool.name:wenchuanyi` +- 浏览器验证 `https://wenchuanyi.bbitcn.net`: + 1. 首页正常显示; + 2. `/#/pickup`、`/#/admin` 路由可访问(hash 路由不受 IIS 影响); + 3. 上传一个测试文件 → 得到取件码 → 取件页下载成功 → 管理码列表可见。 + +--- + +## 6. 验证清单 + +| 项 | 验证方式 | 预期 | +| --- | --- | --- | +| 首页静态资源 | 访问 `/` | 页面正常渲染,无 404 | +| SPA 路由 | 访问 `/#/pickup` | 取件页正常(hash 路由不受 IIS 影响) | +| API | `GET /api/open/download/000000` | 返回 JSON(凭证不存在提示),非 404 | +| 上传下载 | 上传 → 取件 → 下载 | 全流程通过,文件名还原 | +| 在线预览 | 文本/PDF/图片/音视频 | 预览正常 | +| 清理任务 | 查看日志 | 每 30 分钟扫描,无异常报错 | + +--- + +## 7. 常见问题 + +| 现象 | 原因与处理 | +| --- | --- | +| 502.5 / 500.30 | Hosting Bundle 未装或版本不匹配;或 appsettings.json 语法错误。检查事件查看器与 stdoutLog。 | +| 403.14 | 站点根目录无默认文档且 ANCM 未接管——确认 web.config 在站点根目录。 | +| 404 静态资源 | 前端产物未上传到站点根目录,或未启用 `UseStaticFiles`。 | +| API 连接 MySQL 失败 | 服务器 3306 未放行 / 白名单未加服务器出口 IP。 | +| 上传 OSS 失败 | OSS AK/SK 未填或权限不足;确认 Bucket `bbit-f8-web` 有 Put/Get/Delete 权限。 | +| 修改不生效 | 上传后未回收应用程序池。 | + +--- + +## 8. 安全提示 + +- `appsettings.json` 含数据库密码与 OSS 凭据,**不要上传到代码仓库 / 不要外传**(已在 .gitignore)。 +- FTP 凭据仅运维持有,建议定期轮换。 +- 日志不会打印 OSS 凭据、数据库密码与文件内容明文。 diff --git a/deploy/ftp_force_one.ps1 b/deploy/ftp_force_one.ps1 new file mode 100644 index 0000000..0134c8a --- /dev/null +++ b/deploy/ftp_force_one.ps1 @@ -0,0 +1,20 @@ +# 强制覆盖单个文件 (UTF-8 BOM) +param([string]$LocalPath, [string]$RemotePath) +$FtpHost="116.198.221.125" +$FtpPort=21 +$FtpUser="wenchuanyi" +$FtpPass="r7P^f*v7rFts" +$r=[System.Net.FtpWebRequest]::Create("ftp://${FtpHost}:${FtpPort}/"+$RemotePath.TrimStart('/')) +$r.Method=[System.Net.WebRequestMethods+Ftp]::UploadFile +$r.Credentials=New-Object System.Net.NetworkCredential($FtpUser,$FtpPass) +$r.UsePassive=$true +$r.UseBinary=$true +$r.KeepAlive=$false +$r.Timeout=120000 +$b=[System.IO.File]::ReadAllBytes($LocalPath) +$r.ContentLength=$b.Length +$s=$r.GetRequestStream() +try{ $s.Write($b,0,$b.Length) }finally{ $s.Close() } +$resp=$r.GetResponse() +try{ $resp.Close() }catch{} +Write-Host "FORCED_UPLOAD_OK $RemotePath" diff --git a/deploy/ftp_upload.ps1 b/deploy/ftp_upload.ps1 new file mode 100644 index 0000000..2b1dc28 --- /dev/null +++ b/deploy/ftp_upload.ps1 @@ -0,0 +1,59 @@ +# FTP 上传脚本 (UTF-8 BOM) - 健壮版 +$FtpHost="116.198.221.125" +$FtpPort=21 +$FtpUser="wenchuanyi" +$FtpPass="r7P^f*v7rFts" +$RemoteRoot="/" +$Stage = Join-Path $env:TEMP "wcy_publish_staging" + +function New-FtpReq([string]$path,[string]$method){ + $uri="ftp://${FtpHost}:${FtpPort}/"+$path.TrimStart('/') + $r=[System.Net.FtpWebRequest]::Create($uri) + $r.Method=$method + $r.Credentials=New-Object System.Net.NetworkCredential($FtpUser,$FtpPass) + $r.UsePassive=$true + $r.UseBinary=$true + $r.KeepAlive=$false + $r.Timeout=120000 + return $r +} +function MkDir([string]$p){ + $r=New-FtpReq $p ([System.Net.WebRequestMethods+Ftp]::MakeDirectory) + try{ $null=$r.GetResponse() }catch{} +} +function Exist([string]$p){ + $r=New-FtpReq $p ([System.Net.WebRequestMethods+Ftp]::GetDateTimestamp) + try{ $null=$r.GetResponse(); return $true }catch{ return $false } +} +function UpFile([string]$lp,[string]$rp){ + $r=New-FtpReq $rp ([System.Net.WebRequestMethods+Ftp]::UploadFile) + $b=[System.IO.File]::ReadAllBytes($lp) + $r.ContentLength=$b.Length + $s=$r.GetRequestStream() + try{ $s.Write($b,0,$b.Length) }finally{ $s.Close() } + $resp=$r.GetResponse() + try{ $resp.Close() }catch{} +} +function UpDir([string]$ld,[string]$rd){ + foreach($d in Get-ChildItem $ld -Directory){ + $c="$rd/$($d.Name)" + if(-not (Exist $c)){ MkDir $c } + UpDir $d.FullName $c + } + foreach($f in Get-ChildItem $ld -File){ + $rf="$rd/$($f.Name)" + try{ + if(-not (Exist $rf)){ UpFile $f.FullName $rf; Write-Host "UP $rf" } + else { Write-Host "SKIP(exists) $rf" } + }catch{ + Write-Host "RETRY $rf" + try{ UpFile $f.FullName $rf; Write-Host "OK $rf" }catch{ Write-Host "FAIL $rf : $_" } + } + } +} + +if(-not (Test-Path $Stage)){ Write-Host "STAGING_MISSING"; exit 1 } +if(-not (Exist $RemoteRoot)){ MkDir $RemoteRoot } +UpDir $Stage $RemoteRoot +Write-Host "FTP_UPLOAD_DONE" +Remove-Item $Stage -Recurse -Force diff --git a/deploy/publish.ps1 b/deploy/publish.ps1 new file mode 100644 index 0000000..700b47e --- /dev/null +++ b/deploy/publish.ps1 @@ -0,0 +1,119 @@ +# ============================================================ +# 文传易 一键发布脚本 +# 1. dotnet publish 后端 (Release, net8.0) +# 2. npm run build 前端 (Vite, dist) +# 3. FTP 上传合并产物至 IIS 站点根目录 +# +# 用法: +# powershell -ExecutionPolicy Bypass -File deploy/publish.ps1 +# +# 前置: 本机已安装 .NET 8 SDK / Node.js >=18; FTP 凭据见下方变量 +# ============================================================ +param( + [string]$FtpHost = "116.198.221.125", + [int] $FtpPort = 21, + [string]$FtpUser = "wenchuanyi", + [string]$FtpPass = "r7P^f*v7rFts", + [string]$RemoteRoot = "/", # FTP 远端目标目录 (IIS 站点根目录) + [switch]$SkipBuild # 跳过构建, 仅上传已有产物 +) + +$ErrorActionPreference = "Stop" +$Root = Split-Path -Parent $PSScriptRoot +$BackendDir = Join-Path $Root "backend\WenChuanyi.Api" +$FrontendDir = Join-Path $Root "frontend" +$PublishDir = Join-Path $BackendDir "bin\Release\net8.0\publish" +$DistDir = Join-Path $FrontendDir "dist" +$StagingDir = Join-Path $env:TEMP "wcy_publish_staging" + +# ---------------- 1. 构建 ---------------- +if (-not $SkipBuild) { + Write-Host "==> [1/3] 构建后端..." -ForegroundColor Cyan + Push-Location $BackendDir + try { dotnet publish -c Release --nologo -v q } + finally { Pop-Location } + + Write-Host "==> [2/3] 构建前端..." -ForegroundColor Cyan + if (-not (Test-Path (Join-Path $FrontendDir "node_modules"))) { + Write-Host " 首次构建, 安装 npm 依赖..." -ForegroundColor Yellow + Push-Location $FrontendDir + try { npm install --no-audit --no-fund } + finally { Pop-Location } + } + Push-Location $FrontendDir + try { npm run build } + finally { Pop-Location } +} else { + Write-Host "==> [1-2/3] 跳过构建 (SkipBuild)..." -ForegroundColor Yellow +} + +if (-not (Test-Path $PublishDir)) { throw "后端发布目录不存在: $PublishDir (请先 dotnet publish)" } +if (-not (Test-Path $DistDir)) { throw "前端产物目录不存在: $DistDir (请先 npm run build)" } + +# ---------------- 2. 合并产物到临时目录 ---------------- +Write-Host "==> [3/3] 合并产物并 FTP 上传..." -ForegroundColor Cyan +if (Test-Path $StagingDir) { Remove-Item $StagingDir -Recurse -Force } +New-Item -ItemType Directory -Path $StagingDir -Force | Out-Null + +Copy-Item "$DistDir\*" $StagingDir -Recurse -Force +Copy-Item "$PublishDir\*" $StagingDir -Recurse -Force + +Write-Host " 产物合计: $((Get-ChildItem $StagingDir -File -Recurse | Measure-Object).Count) 个文件" -ForegroundColor Gray + +# ---------------- FTP 工具函数 ---------------- +function New-FtpRequest([string]$path, [string]$method) { + $uri = "ftp://${FtpHost}:${FtpPort}/" + $path.TrimStart('/') + $req = [System.Net.FtpWebRequest]::Create($uri) + $req.Method = $method + $req.Credentials = New-Object System.Net.NetworkCredential($FtpUser, $FtpPass) + $req.UsePassive = $true + $req.UseBinary = $true + $req.KeepAlive = $false + return $req +} + +function New-FtpDirectory([string]$remotePath) { + $req = New-FtpRequest $remotePath ([System.Net.WebRequestMethods+Ftp]::MakeDirectory) + try { $null = $req.GetResponse() } + catch { } # 目录已存在时报错, 忽略 +} + +function Send-FtpFile([string]$localPath, [string]$remotePath) { + $req = New-FtpRequest $remotePath ([System.Net.WebRequestMethods+Ftp]::UploadFile) + $bytes = [System.IO.File]::ReadAllBytes($localPath) + $req.ContentLength = $bytes.Length + $stream = $req.GetRequestStream() + try { + $stream.Write($bytes, 0, $bytes.Length) + } finally { + $stream.Close() + } + $resp = $req.GetResponse() + try { $resp.Close() } catch { } +} + +function Send-FtpDirectory([string]$localDir, [string]$remoteDir) { + foreach ($dir in Get-ChildItem $localDir -Directory) { + $child = "$remoteDir/$($dir.Name)" + New-FtpDirectory $child + Send-FtpDirectory $dir.FullName $child + } + foreach ($file in Get-ChildItem $localDir -File) { + $remoteFile = "$remoteDir/$($file.Name)" + Send-FtpFile $file.FullName $remoteFile + Write-Host " UP $remoteFile" -ForegroundColor Gray + } +} + +# ---------------- 上传 ---------------- +New-FtpDirectory $RemoteRoot +Send-FtpDirectory $StagingDir $RemoteRoot + +Remove-Item $StagingDir -Recurse -Force + +Write-Host "" +Write-Host "============================================================" -ForegroundColor Green +Write-Host " 发布完成! 请手动回收 IIS 应用程序池使新版本生效:" -ForegroundColor Green +Write-Host " IIS 管理器 -> 应用程序池 -> wenchuanyi -> 右键回收" -ForegroundColor Green +Write-Host " 或: appcmd recycle apppool /apppool.name:wenchuanyi" -ForegroundColor Green +Write-Host "============================================================" -ForegroundColor Green diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..311a5b7 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.local +.env diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..64479f9 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,20 @@ + + + + + + + 文传易 - 免登录,传文件,真容易 + + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..548d94f --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3160 @@ +{ + "name": "wenchuanyi-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wenchuanyi-frontend", + "version": "1.0.0", + "dependencies": { + "axios": "^1.7.9", + "qrcode": "^1.5.4", + "tdesign-vue-next": "^1.10.5", + "vue": "^3.5.13", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "@types/qrcode": "^1.5.5", + "@vitejs/plugin-vue": "^5.2.1", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.2", + "vite": "^5.4.11" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.5.tgz", + "integrity": "sha512-jfkGfTwhQpsiSckPF8r9bU3pn3vyd72NlWaO+TgEO6WPSDnUhXzrNYCHBMOYj0ACaUgjm6eERLF+XV9a6RstoA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.5.tgz", + "integrity": "sha512-oGVqyQlxnrz9/ty89oHpU857VUHEl5/Xu4R2lS+aivCTrNnSsbiENzTnNaBsjxH0CNWGPhzHArOLFwo+oKXveA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.5.tgz", + "integrity": "sha512-bW7B8xMEq8n99Q3ieEcPRGuphurdZAaFzQc9Efyyw3FL6DZO6pMy9xhdN+kBoD7Sy05xNXSr4OyPPnpkYriS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.5.tgz", + "integrity": "sha512-YSwBS86QeHOGlrxJ1PSOIZSkzRL/JmKeunhc+lV6M1a6En8QuVCD/T/qIA0J4Gd2Y86RIOBYrLcOUtqGh9+/1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.5.tgz", + "integrity": "sha512-2fST8lILgl7cKbme/1KDdPCmbXbG+gqoV3bHp19L0ypX/3akYMBVdOunPleRCwonoLnXOZ/0F+Mt/v8POFmfcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.5.tgz", + "integrity": "sha512-cpIxQCP9J+EVad0a6LO1kY3ZGODlk80VlI+2I96B8xMcdHZ4pLVhfQ49JFpYqjPF91FFkQWftf57YlDcTiw9yQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.5.tgz", + "integrity": "sha512-r9fGh3eFs3e/udWh5ZjXQtxiYK/xoFxQaYR/cELxac/Udkl5Th+IsFm0CX3Kl9hmUH/we7EoMpjJgeQNnE0+IA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.5.tgz", + "integrity": "sha512-xdvFdp7OM6KLJviJT2g/YuRSUjnZgGHk4RNgwIbN7X6cPugOucV60DdHXWzsBVCUdrGb6qSXnJQrrAKMmQuj3Q==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.5.tgz", + "integrity": "sha512-rRqILAndyzHzP7T9NFQrq+4HFWNhqkqkKur7eiBpfLmz01PO0JKx5Vchu3YllE4YXI/Ftgq/szrDWg5GJ0mI8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.5.tgz", + "integrity": "sha512-Gf4X3qVMucayUvux6aXXPgXovocSFUC0rrffDuPI/S2nHhNMhjcZxsrAFYCOF350PRreW1XwzFj3CT/3bKsWCw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.5.tgz", + "integrity": "sha512-+s5qA0TNM0qm8PK/a5gt/1Hpx+NV08uSuCncvhziIlQzT6AEV2fnUQo7eBtFTFO0nA9scauvoR2HusfXmQnO4w==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.5.tgz", + "integrity": "sha512-ybb6QvWwWJCbBWqERpc8K3pYVGIrXlG8MEQ8IIuJY6Y9KdHQxoFoNyfkAOtKn1VHu3KuLidXvwrvGR1mEjeWCw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.5.tgz", + "integrity": "sha512-nZb1DtnOyhCmYvsC8A2CwOkopVg+IS1+fPUa7rMOAXtNw5+lLCLLPqd6XAiNrGtoQKsbvIBOwsHnBH/3wnb4HQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.5.tgz", + "integrity": "sha512-yMbj63Sp89ryrXLWyz+sy+fYD2HpOnMCLGbe4Oa1smclFSUukdtD/BgdiHaAetJNb74URD8U4hM+qG5KVzMEkg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.5.tgz", + "integrity": "sha512-mhoan3OJw2kYV/e1jtIdmvUZgyBFeA6zGWsOswmR0Tg19TQbowZuR+JMLID6spbbBN7Zee2ejrgmy3+FxGrIdA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.5.tgz", + "integrity": "sha512-5ZTLmjWbb1VZdjuyhe83K/8QO0/h11midQCBP+X5OYn32ra7eOBoM0ZqtaY4nkgNsYgmdVhMYPoyVPTjUpHf3w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.5.tgz", + "integrity": "sha512-m53kG+br6PGxOTmgBEM2DHSDs9RVjsyEbUwjJPJGTFm1grWOG8EKJggDCTb60unD4Tjby8fi7/m9XfkEWasVWg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.5.tgz", + "integrity": "sha512-6RHPJR1g/uvdYU8uXBnfq3nlqyZCP82Fr6NHgfGoaIeSh0YEqnX/x6uA9MmJJbnSH7swqX4F+CkGdUF+6doiQA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.5.tgz", + "integrity": "sha512-xs+OXQtEXgpXT0DmA5+U3qnRZHdCST/5HRQxS8wSPZTUZN/EMWeHuSIod32LQklTBZBV9DyfncKBQ8n5V3eFdw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.5.tgz", + "integrity": "sha512-e7hD+sl3s+mcLQDZ8pbudBVsdG6r5yN4w3LqG2TJ8sQHDpblWj5lrJs/3m01Cvlxbt4x13zu5thLjgypgtkYzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.5.tgz", + "integrity": "sha512-GiyJaCf+WpMub/17aPcKk27QMl5W6f+KhdPTjlFOn5akH5Wa/DCM9Stdx5cDfmasyKB08MqpVQ1uJE2RkkpbXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.5.tgz", + "integrity": "sha512-+OQ8U2DdoEfXl8T4Fb18AjmEwbXMerKDKCL8yCPAYhKCEEKoul7rkbeGCBFCbAlaGaa7pmtRTpkAJM2LE/i5FA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.5.tgz", + "integrity": "sha512-KanvAZrPKbDBFwrgiU9yEVpQoox9QPV1WZOXX7HudJQY+eSlu82CtWxDU8WtuRRvtN5EGkLczkd6Y6DTcvm9wA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.5.tgz", + "integrity": "sha512-1aC3UEWTtRl3RK3VpDJ/Tqk1XI4SLTmXIthAq6wRWo8XiSXJNd+VprJM4/1P4+i6HIaFEFlVi9sTTziniD2tOQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.5.tgz", + "integrity": "sha512-/gDJaRs4gl0NPIwqCz+6PkpmhhjRAD2j6P4rSNHBzUkO3naEx2mIU0pRle1vUNRQ7mE/+8OOeXLTv/J56FKiQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz", + "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/sortablejs": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.9.tgz", + "integrity": "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==", + "license": "MIT" + }, + "node_modules/@types/tinycolor2": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz", + "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==", + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz", + "integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.41", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz", + "integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz", + "integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.41", + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-ssr": "3.5.41", + "@vue/shared": "3.5.41", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz", + "integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.41.tgz", + "integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.41.tgz", + "integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz", + "integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.41", + "@vue/runtime-core": "3.5.41", + "@vue/shared": "3.5.41", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.41.tgz", + "integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/shared": "3.5.41" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz", + "integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==", + "license": "MIT" + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz", + "integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.6", + "caniuse-lite": "^1.0.30001806", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.18.tgz", + "integrity": "sha512-1iEmLEYSiE1SeBoAfPo/Mnx3PzfzHUkDK61ASkCpuk3YXugYLH5DYK1SzqV55F8FMI6s0F+/tCP7Polz1QRjxw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001809", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.412", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz", + "integrity": "sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.2.tgz", + "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.62.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.5.tgz", + "integrity": "sha512-/tqMfgP7GPA3PHhCmuiS4vIjrSVhHLgY++i+dhbG462euyAj7FpM4D9uq1X3BgjlqRdpcOrYhcQtfiQLNc8tqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.5", + "@rollup/rollup-android-arm64": "4.62.5", + "@rollup/rollup-darwin-arm64": "4.62.5", + "@rollup/rollup-darwin-x64": "4.62.5", + "@rollup/rollup-freebsd-arm64": "4.62.5", + "@rollup/rollup-freebsd-x64": "4.62.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.5", + "@rollup/rollup-linux-arm-musleabihf": "4.62.5", + "@rollup/rollup-linux-arm64-gnu": "4.62.5", + "@rollup/rollup-linux-arm64-musl": "4.62.5", + "@rollup/rollup-linux-loong64-gnu": "4.62.5", + "@rollup/rollup-linux-loong64-musl": "4.62.5", + "@rollup/rollup-linux-ppc64-gnu": "4.62.5", + "@rollup/rollup-linux-ppc64-musl": "4.62.5", + "@rollup/rollup-linux-riscv64-gnu": "4.62.5", + "@rollup/rollup-linux-riscv64-musl": "4.62.5", + "@rollup/rollup-linux-s390x-gnu": "4.62.5", + "@rollup/rollup-linux-x64-gnu": "4.62.5", + "@rollup/rollup-linux-x64-musl": "4.62.5", + "@rollup/rollup-openbsd-x64": "4.62.5", + "@rollup/rollup-openharmony-arm64": "4.62.5", + "@rollup/rollup-win32-arm64-msvc": "4.62.5", + "@rollup/rollup-win32-ia32-msvc": "4.62.5", + "@rollup/rollup-win32-x64-gnu": "4.62.5", + "@rollup/rollup-win32-x64-msvc": "4.62.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/sortablejs": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz", + "integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tdesign-icons-vue-next": { + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/tdesign-icons-vue-next/-/tdesign-icons-vue-next-0.4.10.tgz", + "integrity": "sha512-TOmy4cP5tXiLk5/rC6ZheRw+qeokqTytl4HLE+7qZYc185kPq659ATyn9a1i+c5ZSU3eHPY79kIaJUEOl7OoyQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.16.5" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/tdesign-vue-next": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/tdesign-vue-next/-/tdesign-vue-next-1.20.6.tgz", + "integrity": "sha512-k9pKaWyyCPv1RGO8YEqhmOoAkS/WZ+CbS0CLWGMtuIltS+363dCZ4ggKhmu/y7Zvnv6Wdzoy8R5+ZIsuwsdvkw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.22.6", + "@popperjs/core": "^2.11.8", + "@types/lodash-es": "^4.17.12", + "@types/sortablejs": "^1.15.1", + "@types/tinycolor2": "^1.4.3", + "@types/validator": "^13.7.17", + "dayjs": "^1.11.10", + "lodash-es": "^4.18.1", + "mitt": "^3.0.1", + "sortablejs": "^1.15.0", + "tdesign-icons-vue-next": "^0.4.9", + "tinycolor2": "^1.6.0", + "validator": "^13.15.23" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "vue": ">=3.1.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.41", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.41.tgz", + "integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.41", + "@vue/compiler-sfc": "3.5.41", + "@vue/runtime-dom": "3.5.41", + "@vue/server-renderer": "3.5.41", + "@vue/shared": "3.5.41" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..9792a30 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,28 @@ +{ + "name": "wenchuanyi-frontend", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "axios": "^1.7.9", + "qrcode": "^1.5.4", + "tdesign-vue-next": "^1.10.5", + "vue": "^3.5.13", + "vue-router": "^4.5.0" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "@types/qrcode": "^1.5.5", + "@vitejs/plugin-vue": "^5.2.1", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.2", + "vite": "^5.4.11" + } +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..c390018 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,100 @@ + + + diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 0000000..701d0af --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1,75 @@ +import axios from 'axios' + +export interface FileInfo { + id: number + pickCode: string + fileType: 'standard' | 'private' | 'tagged' + hasPassword: boolean + originalName: string + size: number + mimeType: string | null + downloadCount: number + uploadIp: string | null + isPermanent: boolean + expiresAt: string | null + createdAt: string + expireText: string +} + +export interface UploadResult { + id: number + pickCode: string + adminCode: string + fileType: string + originalName: string + size: number + isPermanent: boolean + expiresAt: string | null + pickUrl: string +} + +const api = axios.create({ + baseURL: '/api', + timeout: 5 * 60 * 1000, +}) + +// ---------- 上传 ---------- +export function uploadFile(data: FormData, onProgress?: (p: number) => void) { + return api.post('/files/upload', data, { + onUploadProgress: (e) => { + if (onProgress && e.total) onProgress(Math.round((e.loaded / e.total) * 100)) + }, + }) +} + +// ---------- 取件 ---------- +export function queryFile(code: string, password?: string) { + return api.get(`/files/${code}`, { params: password ? { password } : {} }) +} + +export function listByTag(tag: string) { + return api.get(`/files/by-tag/${encodeURIComponent(tag)}`) +} + +export function downloadUrl(code: string, password?: string) { + return `/api/files/${code}/download${password ? `?password=${encodeURIComponent(password)}` : ''}` +} + +export function previewUrl(code: string, password?: string) { + return `/api/files/${code}/preview${password ? `?password=${encodeURIComponent(password)}` : ''}` +} + +export function contentUrl(code: string, password?: string) { + return `/api/files/${code}/content${password ? `?password=${encodeURIComponent(password)}` : ''}` +} + +// ---------- 管理 ---------- +export function adminList(adminCode: string) { + return api.get(`/admin/files/${adminCode}`) +} + +export function adminDelete(adminCode: string, id?: number, pickCode?: string) { + return api.delete(`/admin/files/${adminCode}`, { data: { id, pickCode } }) +} + +export default api diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts new file mode 100644 index 0000000..65c7311 --- /dev/null +++ b/frontend/src/env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent + export default component +} diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000..b5c715f --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,64 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --color-primary: #2d6cff; + --color-primary-light: #00b4ff; + --color-bg: #f4f7fc; +} + +* { + -webkit-tap-highlight-color: transparent; +} + +html, +body { + @apply m-0 p-0; + font-family: 'PingFang SC', 'Microsoft YaHei', system-ui, -apple-system, sans-serif; + background: var(--color-bg); + color: #1f2329; + overscroll-behavior-y: none; +} + +#app { + min-height: 100vh; +} + +/* 页面淡入 */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.25s ease; +} +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} + +/* 通用卡片 */ +.wcy-card { + @apply bg-white rounded-2xl shadow-card; +} + +/* 渐变文字 */ +.text-gradient { + background: linear-gradient(120deg, #2d6cff 0%, #00b4ff 100%); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +/* 等宽数字(凭证) */ +.code-font { + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + letter-spacing: 0.08em; +} + +/* 悬浮上浮微动效 */ +.hover-lift { + @apply transition-all duration-200; +} +.hover-lift:hover { + transform: translateY(-2px); + box-shadow: 0 12px 32px rgba(45, 108, 255, 0.16); +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..c37f23e --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,8 @@ +import { createApp } from 'vue' +import TDesign from 'tdesign-vue-next' +import 'tdesign-vue-next/es/style/index.css' +import './index.css' +import App from './App.vue' +import { router } from './router' + +createApp(App).use(TDesign).use(router).mount('#app') diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts new file mode 100644 index 0000000..e1e858b --- /dev/null +++ b/frontend/src/router/index.ts @@ -0,0 +1,13 @@ +import { createRouter, createWebHashHistory } from 'vue-router' +import UploadView from '@/views/UploadView.vue' +import PickupView from '@/views/PickupView.vue' +import AdminView from '@/views/AdminView.vue' + +export const router = createRouter({ + history: createWebHashHistory(), + routes: [ + { path: '/', component: UploadView, meta: { title: '上传', icon: 'cloud-upload' } }, + { path: '/pickup', component: PickupView, meta: { title: '取件', icon: 'link' } }, + { path: '/admin', component: AdminView, meta: { title: '管理', icon: 'setting' } }, + ], +}) diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue new file mode 100644 index 0000000..75f0fa1 --- /dev/null +++ b/frontend/src/views/AdminView.vue @@ -0,0 +1,145 @@ + + + diff --git a/frontend/src/views/PickupView.vue b/frontend/src/views/PickupView.vue new file mode 100644 index 0000000..c695182 --- /dev/null +++ b/frontend/src/views/PickupView.vue @@ -0,0 +1,450 @@ +