[Ai] feat: 花魁短剧 Next.js 站点初始提交
- 原生 Next.js App Router + 严格 TypeScript 实现花魁短剧 PC/H5 页面,按 src/features/{catalog,content,playback} 组织,保留完整 SEO(metadata、JSON-LD、canonical、robots、llms、sitemap、IndexNow)。
- 主域名通过 SITE_URL=https://huakuidj.com 强制覆盖后端 foreverUrlAddr(config/*.env、src/server/config.ts、src/server/seo/shared.ts、src/server/services/shared.ts),支持邮箱改为 support@huakuidj.com。
- 移除登录与账户体系:删除 /account 全部页面、登录/注册/充值弹窗、收藏点赞、评论、金币解锁及对应 BFF 接口;旧账户地址 GET/HEAD 统一 301 到首页(src/lib/routes.ts REMOVED_ACCOUNT_PATHS),保留游客会话、反馈、图片上传、观看上报与搜索联想。
- 播放页:评论位改为同类推荐 + 热播榜单,锁定集改为 App 观看遮罩与底部弹层,新增简介标签、分集图例与独立视觉皮肤(src/features/playback、src/styles/pages/playback.css)。
- 卡片:短剧/视频卡状态与时长移至信息行胶囊标签,封面角标移至左上角(src/components/catalog/Cards.tsx、src/styles/foundations/base.css)。
- 同步更新单元测试、浏览器冒烟脚本、路由清单与 README。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@@ -0,0 +1,14 @@
|
||||
.git
|
||||
node_modules
|
||||
.next*
|
||||
dist
|
||||
dist.zip
|
||||
dist.zip.sha256
|
||||
web_hkdj_h5.tar.gz
|
||||
web_hkdj_h5.md5
|
||||
config/*.local.env
|
||||
config/secrets
|
||||
test-results
|
||||
playwright-report
|
||||
*.log
|
||||
*.tsbuildinfo
|
||||
@@ -0,0 +1,20 @@
|
||||
node_modules/
|
||||
.next*/
|
||||
*.tsbuildinfo
|
||||
.env.local
|
||||
.env.*.local
|
||||
config/*.local.env
|
||||
config/secrets/
|
||||
coverage/
|
||||
test-results/
|
||||
playwright-report/
|
||||
.DS_Store
|
||||
*.log
|
||||
dist/
|
||||
dist.zip
|
||||
dist.zip.sha256
|
||||
web_hkdj_h5.tar.gz
|
||||
web_hkdj_h5.md5
|
||||
/template/pages/sitemaps/*.xml
|
||||
/template/pages/sitemap.xml
|
||||
/data/sitemaps/*.xml
|
||||
@@ -0,0 +1,31 @@
|
||||
<!-- BEGIN:nextjs-agent-rules -->
|
||||
|
||||
## This is NOT the Next.js you know
|
||||
|
||||
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
|
||||
|
||||
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
|
||||
|
||||
<!-- END:nextjs-agent-rules -->
|
||||
|
||||
## 花魁短剧重构记忆
|
||||
|
||||
- 本仓库是花魁短剧原生 Next.js App Router + 严格 TypeScript 项目,原站参考目录为 `/Users/mac/Desktop/work/web/jha-1045-hgdjnet-web`。页面必须使用 React/TSX;不要恢复 Go/HTML 模板解析或 `dangerouslySetInnerHTML` 页面适配器。
|
||||
- 保持原站功能、PC/H5 界面和完整 SEO 行为:metadata、JSON-LD、canonical、分页、robots、llms、sitemap、站点验证、IndexNow,以及重定向/404/503 的状态和索引规则。
|
||||
- 保留服务端 API 转发、请求头和 Cookie 透传、游客会话恢复、多条 Set-Cookie 和私有响应禁止缓存。不要把鉴权或 SDK 私密配置泄露到公开数据、日志或图片 CDN。
|
||||
- 页面按 `src/features/{catalog,account,content,playback}` 组织;页头/页尾、卡片、图片解密、播放器、鉴权、支付及埋点优先复用 `src/components`、`src/lib`、`src/server` 中的公共实现。
|
||||
- 业务 CSS 统一在 `src/styles/{foundations,components,pages,overrides}`,通过 TSX 顶层导入并由 Next 编译为哈希资源;不要恢复 `/static/css` 或手工 stylesheet 链接。业务类名使用 `hk-` 与语义组件名称,xgplayer 第三方类名保留;页面样式使用零特异性 `html[data-hk-page-styles]` 隔离,PWA 最后加载。客户端切页由 `PagePresentation` 同步作用域与页面 body class,不可覆盖滚动锁/PWA/临时状态。
|
||||
- 2026-10-14 用户明确重新启用语义新路径并同步全部引用,覆盖此前回退要求。代码入口和对外 URL 使用 `/dramas`、`/drama`、`/series/watch`、`/collections`、`/collection`、`/account`、`/navigation` 等;旧有效页面 GET/HEAD 一次 301 到新路径,规则统一在 `src/lib/routes.ts`。页面文件使用 `HomePage`、`BrowsePages`、`DiscoveryPages`、`DramaWatchPage`、`SeriesWatchPage`、`SettingsPage`、`SiteNavigationPage`,服务端播放模块为 `src/server/pages/playback.ts`。站内链接、metadata、分页、JSON-LD、llms、安装默认入口使用新路径,API/媒体签名/外链保持原合同与编码。`npm run routes:export -- --admin ../hkdj-web-admin` 从实际路由生成全部新旧对照及后台路径清单,`routes:check` 核验无漂移;后台导航分类支持选择静态入口或自由输入,参数模板不作为可选链接。后端 `hkdj-web-server` 的 h5path、sitemap 与 SEO 推送源同步新路径,Next 仍原字节服务 XML,不在请求中改写生成结果。
|
||||
- robots/llms/sitemap 完整遵循参考 Go 合同:robots/llms 读取公共配置并使用原永久域名/代理 Host 规则,robots 保留四个磁盘候选与内嵌文本的优先级及原字符串处理。sitemap 只服务后端生成的文件;开发 `SITEMAP_DIR=./template/pages/sitemaps`,测试/生产 `SITEMAP_DIR=../template/pages/sitemaps`,仅作为部署目录映射。测试 Node 工作目录 `/server/hkdj_net/h5/next`,真实产物在父级 `h5/template/pages/sitemaps`,生产遵循相同目录关系;索引其次读取 `./template/pages/sitemap.xml`。请求不读取远端 sitemap、不生成静态回退或合成索引、不改写 XML;缺失/非法文件走原完整 404 页面及共享数据加载。开发仅可显式 `npm run sitemap:sync` 通过可信 SSH 只读同步索引引用的完整分片,保留原字节/mtime,不修改远端、不删除未知文件;开发 XML 忽略提交,页面 trace 排除;Next 16.4 Node Proxy trace 仍可能带入原始 standalone,必须通过 bundle 脚本在隔离 dist 精确清理 XML,并逐层 lstat 拒绝符号链接父目录,保留 robots/key/未知文件和后端源目录。导航模式与 Go `DHInit` 一致,不注册抓取文件/验证/API/媒体路由。发布保留后端 XML,持续写入的原目录应外置挂载,不能随前端包清空。
|
||||
- 成功抓取文件通过原生 Route Handler 返回;Proxy 仅选择成功出口或准备并改写完整 TSX 404,不能直接从 Proxy 输出文件,否则 Next 会过滤 Content-Length。保留 Go ServeFile 的 MIME、原字节、Last-Modified、Content-Length、Accept-Ranges、304/412/206/416、If-Range/多段 Range 和 HEAD;Next compress=false 避免框架自动压缩改变文件响应,外层代理可沿用原传输配置。
|
||||
- 播放器通过 npm 使用 `xgplayer` 与 `xgplayer-hls`;不要恢复 CDN 脚本版 DPlayer/Hls/Swiper 或旧业务脚本。
|
||||
- `/h5/m3u8/**`、`/enkey`、`/newenkey` 保留服务端媒体转发:签名 query 不重组,UA/IP/Range 保留,上游重定向按原 Go 客户端跟随。签名媒体请求按原合同不附登录 Cookie/Authorization;上游业务 JSON 错误返回 502,不能当播放清单成功响应。
|
||||
- `config/development.env`、`config/test.env`、`config/production.env` 分别管理三套环境,本地覆盖使用忽略提交的 `*.local.env`;进程环境优先。开发 API 按用户指定保持 `http://172.104.181.99:30331`,不要擅自切回原站 25319。
|
||||
- SSR 请求准备在 `src/proxy.ts` 完成,通过 `src/server/react/handoff.ts` 向 React 交接;保留同请求 Cookie/页面数据一致性。大快照使用私有临时文件,部署多实例时须处理交接存储共享或实例亲和。
|
||||
- 所有“黄果短剧”统一为“花魁短剧”。占位图品牌 logo 透明度固定为 `0.2`。Figma 文件为 `1vUq4wf5xOs8ZOlmt26Q3O`,读取设计使用本地 `FIGMA_TOKEN` 调用 REST API,不能打印令牌。
|
||||
- 本地 `npm run dev` 默认使用 Webpack;该项目已确认 Next 16.4 Turbopack 开发 HMR 存在循环刷新,未经验证不要改回默认。Charles 调试使用 `npm run dev:charles`,不改业务接口地址。
|
||||
- 验证至少覆盖 `npm run check` 和受影响的浏览器链路;图片必须检查真实接口返回的地址、解密后的自然尺寸、背景图及最终可见性(包含 computed opacity),模拟图片测试通过不能证明 CDN 可用。首页 Hero 封面成功/失败均需结束加载状态,保留淡入和失败占位;不能遗漏 CSS 依赖的就绪标记。
|
||||
- 图片地址由公共 ImageProvider/解密组件统一解析:优先上游 `imgCdn`、`imgAppUsCdn`,再用环境 `IMAGE_CDN_URL`。当前开发 API 缺 CDN,开发环境单独配置已核验的 CloudFront 来源;不要把 API 地址当作图片 CDN。保留原 XOR 协议并验证图片文件头,错误响应不能缓存为图片。
|
||||
- 提交遵循用户全局 AGENTS 规则:无前缀请求使用 `[Ai] <type>: <中文主题>`,正文具体说明文件/模块/资源;仅确有 Claude 共创时添加 Claude trailer。
|
||||
- PWA 安装配置使用 `src/app/manifest.ts` 与 `src/lib/pwa/config.ts`:fullscreen 优先、standalone 回退、orientation any;全局 viewport-fit cover 与 Apple 独立窗口标签不可只放播放器页。Next 16 的 appleWebApp.capable 仅生成非 Apple 前缀标签,需保留 Metadata.other 中的 apple-mobile-web-app-capable。
|
||||
- `/manifest.webmanifest` 与 `/sw.js` 必须绕过 SSR Proxy/游客初始化,包括 NAVIGATION_ONLY;Worker 源码为 TypeScript,开发/构建自动生成 public/sw.js,不拦截或缓存鉴权 API、HTML/RSC、签名媒体及密钥。PWA 安全区样式最后加载,普通浏览器不改变原布局;验证 `npm run test:pwa` 与实际 SSR 清单/标签/资源,真实设备安装需 HTTPS。
|
||||
@@ -0,0 +1,15 @@
|
||||
FROM node:22-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
ARG APP_ENV=production
|
||||
RUN npx tsx scripts/next-env.ts "$APP_ENV" build && npx tsx scripts/package-standalone.ts "$APP_ENV" && mv "dist/$APP_ENV" /release
|
||||
|
||||
FROM node:22-alpine AS runtime
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production PORT=3000 HOSTNAME=0.0.0.0
|
||||
COPY --from=build --chown=node:node /release ./
|
||||
USER node
|
||||
EXPOSE 3000
|
||||
CMD ["node", "start.mjs"]
|
||||
@@ -0,0 +1,364 @@
|
||||
# 花魁短剧 · Next.js / TypeScript
|
||||
|
||||
从 `jha-1045-hgdjnet-web` 迁移的独立 Next.js 16 项目。运行时仅需要 Node.js,不依赖 Go、原项目目录或原 Go 服务。保留内容浏览页面、原 URL、游客会话、播放器和统计交互(登录、账户与充值功能已移除,旧账户地址 301 到首页);PC/H5 界面按花魁短剧 Figma 设计更新。
|
||||
|
||||
## 启动
|
||||
|
||||
推荐 Node.js 22,最低 Node.js 20.9。
|
||||
|
||||
```bash
|
||||
npm ci
|
||||
npm run dev
|
||||
```
|
||||
|
||||
默认开发地址为 `http://localhost:25121`,使用原项目开发 API。通过 `PORT=其他端口 npm run dev` 可以更换端口。开发及测试开发模式默认使用 Webpack 热更新;可用 `NEXT_DEV_BUNDLER=turbopack npm run dev` 选择 Turbopack。优化构建保持 Next 默认 Turbopack。
|
||||
|
||||
## PWA 全屏
|
||||
|
||||
通过 HTTPS 访问站点并安装到主屏幕后,从桌面图标启动。Android 优先采用 `fullscreen`,不支持时回退 `standalone`;iOS 使用独立窗口与透明状态栏,系统状态栏的显示由 Safari/iOS 决定。普通浏览器标签页保持浏览器本身的显示模式。
|
||||
|
||||
`src/app/manifest.ts` 提供安装清单,共用设置位于 `src/lib/pwa/config.ts`,安装入口为 `/`,不添加影响渠道或 canonical 的 query。全局 viewport 使用 `viewport-fit=cover`;安装窗口的页头、底部导航、弹窗与播放器按安全区布局,支持横竖屏。缺少原生 Fullscreen API 的安装窗口使用 xgplayer CSS 全屏。
|
||||
|
||||
`PwaProvider` 只在安全上下文注册 `/sw.js`,不弹安装提示;localhost 可用于开发验证,手机通过普通局域网 HTTP 访问时无法注册 Service Worker。Worker 源码为 TypeScript,开发启动与构建时自动编译,不拦截或离线缓存页面、接口、签名媒体和密钥;`/manifest.webmanifest` 与 `/sw.js` 绕过游客会话和 SSR Proxy,Worker 禁止 HTTP 缓存。standalone 包包含清单路由、Worker 和安装图标。
|
||||
|
||||
安装图标由现有花魁 SVG favicon 等比例生成,另提供保留安全留白的 maskable 图标及 180px Apple 图标。更新源图后运行:
|
||||
|
||||
```bash
|
||||
npm run assets:pwa
|
||||
npm run assets:pwa:check
|
||||
npm run test:pwa
|
||||
```
|
||||
|
||||
`test:pwa` 验证实际 React 组件、Worker 注册、模拟安全区和播放器全屏进出;安装与系统状态栏还需在真实 Android/iOS 设备验证。已安装的旧桌面入口可能保留旧清单,更新后可移除旧入口再安装。
|
||||
|
||||
## 本地 Charles 抓包
|
||||
|
||||
从 `hgdj-official-website` 移植 `dev:charles`,在 Node 启动前设置原生环境代理和 Charles 根证书,让服务端上游 API 和媒体的原生 `fetch` 请求经过 Charles。沿用普通开发的配置优先级、React 热更新和默认端口 `25121`;sitemap 读取本地后端产物,不发起远程下载。
|
||||
|
||||
1. 使用 Node **24.5+**(或 **22.21+**);Node 22.17 不支持此代理开关。
|
||||
2. 打开 Charles,确认 HTTP Proxy 端口为 `8888`,在 Proxy → SSL Proxying Settings 中启用实际 `UPSTREAM_API_URL` 对应的域名及端口,例如 `api.example.com:443`。
|
||||
3. 在 Help → SSL Proxying → Save Charles Root Certificate 中导出 PEM,保存到 `~/.charles-ca.pem`。Charles 重置根证书后需要重新导出。
|
||||
|
||||
```bash
|
||||
nvm use 24
|
||||
npm run dev:charles
|
||||
# 可通过 PORT 或传递 Next 参数指定端口
|
||||
npm run dev:charles -- --port 3001
|
||||
```
|
||||
|
||||
默认代理为 `http://127.0.0.1:8888`,本机回环请求通过 `NO_PROXY` 排除,并保留既有排除项。自定义代理地址或证书:
|
||||
|
||||
```bash
|
||||
CHARLES_PROXY_URL=http://127.0.0.1:8889 \
|
||||
NODE_EXTRA_CA_CERTS="$HOME/certs/charles.pem" \
|
||||
npm run dev:charles
|
||||
```
|
||||
|
||||
代理变量只作用于本次抓包进程;普通开发仍使用 `npm run dev`。这些 Node 启动变量不能仅放进 Next 的 `.env.local` 或 `config/development.local.env`。浏览器直接发出的请求需要浏览器/系统 Charles 代理;缓存命中时不会产生新的上游请求。
|
||||
|
||||
Node 原生代理说明:[Node.js HTTP 文档](https://nodejs.org/download/release/v24.17.0/docs/api/http.html#built-in-proxy-support)。
|
||||
|
||||
## 开发、测试、生产
|
||||
|
||||
| 环境 | 配置文件 | 开发启动 | 构建 | 运行构建 | 构建目录 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 开发 | `config/development.env` | `npm run dev` | `npm run build:dev` | `npm run start:dev` | `.next-dev` |
|
||||
| 测试 | `config/test.env` | `npm run dev:test` | `npm run build:test` | `npm run start:test` | `.next-test` |
|
||||
| 生产 | `config/production.env` | — | `npm run build` | `npm start` | `.next` |
|
||||
|
||||
配置优先级:进程环境变量 > `config/<环境>.local.env` > `config/<环境>.env`。本地覆盖文件不会提交。`APP_ENV` 选择业务环境;测试环境的优化构建仍使用 Next.js 要求的 `NODE_ENV=production`。
|
||||
|
||||
开发 `UPSTREAM_API_URL` 使用 `http://172.104.181.99:30331`;测试服务与 API 同机,使用 `http://127.0.0.1:30331`,测试应用端口为 `30334`。本地运行测试环境且 API 不在本机时,可在 `config/test.local.env` 覆盖上游地址。正式配置使用同机 API `http://127.0.0.1:26931`、服务端口 `26935`,可由部署环境或 `config/production.local.env` 覆盖。构建不访问上游,启动时检查地址。
|
||||
|
||||
```dotenv
|
||||
UPSTREAM_API_URL=https://your-api.example
|
||||
UPSTREAM_TIMEOUT_MS=20000
|
||||
IMAGE_CDN_URL=
|
||||
SITEMAP_DIR=../template/pages/sitemaps
|
||||
TRACKER_APP_ID=JHA-4001
|
||||
TRACKER_ENCRYPTED_CONFIG=
|
||||
```
|
||||
|
||||
这些变量仅供服务端使用。`config/info.reportSdkConfig` 优先于本地埋点密文配置。官方导航独立进程可以使用 `NAVIGATION_ONLY=true PORT=26813 npm start`,开放 `/`、`/navigation/` 和静态资源,原 `/nav/`、`/dh/` 一次 301 到 `/navigation/`。与原 Go `DHInit` 一致,导航模式不注册 robots、llms、sitemap、验证文件及业务接口,这些地址返回原站 404 页面。
|
||||
|
||||
图片 CDN 与 API 独立配置。上游 `config/info.config.imgCdn`、`imgAppUsCdn` 按顺序提供图片来源,`IMAGE_CDN_URL` 提供环境级备用来源。开发与测试共用 API 30331,该接口返回裸 `v3/image/...` 且 CDN 配置为空,因此两套环境均显式配置原站已验证可用的 CloudFront 图片线路;生产独立配置。修改环境配置后需重启服务。
|
||||
|
||||
公共图片组件统一解析相对路径,覆盖封面、横幅、背景和头像;相对路径不会随页面目录变化。图片保持原首 100 字节 XOR 协议,也兼容未加密的有效图片;校验文件头后才缓存,HTTP、证书、JSON 错误或超时会尝试配置的备用 CDN。完整外域 URL 保持原签名,只有属于已配置 CDN 的资源才允许切换相同对象路径;图片请求不向外域发送本站 Cookie/Authorization。
|
||||
|
||||
## 目录和渲染边界
|
||||
|
||||
| 目录 | 职责 |
|
||||
| --- | --- |
|
||||
| `src/app/` | 原生 App Router TSX 页面、Metadata 与 BFF/媒体 Route Handlers |
|
||||
| `src/features/` | 短剧目录、播放、账号与内容页面组件 |
|
||||
| `src/components/` | 页头、页尾、卡片、播放器、图片、弹窗、认证、支付、埋点共用组件 |
|
||||
| `src/styles/` | 基础、公共组件、页面和 PWA 覆盖样式,通过 TSX 导入并由 Next 编译 |
|
||||
| `src/lib/` | 类型化浏览器请求、Cookie、搜索/展示方法与埋点上下文 |
|
||||
| `src/proxy.ts`、`src/server/react/` | SSR 数据、状态、响应 Cookie 和 React 数据交接 |
|
||||
| `src/server/pages/` | 原路由参数、分页、页面数据组合与播放/账号场景 |
|
||||
| `src/server/services/` | 上游 DTO、展示模型、公共配置和请求级复用 |
|
||||
| `src/server/upstream.ts`、`auth.ts` | 上游请求、请求头/Cookie 透传、登录与游客恢复 |
|
||||
| `src/server/seo/` | 原 SEO 策略、TDK、canonical、JSON-LD 和抓取文件 |
|
||||
| `public/static/` | 本地 Figma 品牌/图标、分享图与私有埋点 SDK |
|
||||
|
||||
所有业务页面由 TSX 组件服务端渲染,交互使用 React hooks 和 Client Components。App Router 的 `generateMetadata` 输出完整首屏 SEO;JSON-LD 使用安全序列化的独立脚本。没有 HTML 模板运行时或注入整页 HTML 的适配层。
|
||||
|
||||
54 份业务样式统一在 `src/styles/{foundations,components,pages,overrides}`,由根 `layout.tsx` 顶层导入,播放器同时保留 npm `xgplayer` 样式。Next 输出带内容哈希的 CSS 文件,无需手工版本参数或 `/static/css` 链接。业务类名使用 `hk-` 命名空间和组件/业务名称;第三方播放器类名保留其包合同。页面样式通过零特异性的 `html[data-hk-page-styles]` 作用域隔离,基础和公共组件样式共享,PWA 安全区覆盖最后加载。`PagePresentation` 在客户端切页时同步作用域及页面 body class,同时保留 PWA、弹窗滚动锁等临时状态。
|
||||
|
||||
页面代码目录和公开 URL 按用户最新确认统一使用语义路径。`src/app` 使用 `dramas`、`drama`、`account` 等目录,旧有效页面 GET/HEAD 一次 301 到新地址,规则由 `src/lib/routes.ts` 管理,保留 slug 编码、原始 query 和 hash。站内链接、canonical、分页、JSON-LD、llms 和默认 App 下载入口同步新地址;同产品后端的 sitemap、canonical 与 SEO 推送生成新路径,Next 继续原字节服务后端 XML。API、媒体签名、图片 CDN、支付和外链保持各自合同。
|
||||
|
||||
| 场景 | 当前页面路径 | 301 兼容旧路径 |
|
||||
| --- | --- | --- |
|
||||
| 短剧浏览/播放 | `/dramas/`、`/drama/:slug/` | `/list/`、`/play/:slug/` |
|
||||
| 剧集浏览/播放 | `/series/`、`/series/watch/:slug/` | `/series-play/:slug/` |
|
||||
| 精选/最新/榜单 | `/featured/`、`/latest/`、`/rankings/` | `/popular-recommend/`、`/recently-added/`、`/rank/` |
|
||||
| 专题列表/详情 | `/collections/`、`/collection/:slug/` | `/topic/`、`/topic-detail/:slug/` |
|
||||
| 推荐栏目 | `/curated/dramas/:id/`、`/curated/series/:id/` | `/more-list/:id/`、`/more-series-list/:id/` |
|
||||
| 移动搜索/导航/下载 | `/discover/search/`、`/navigation/`、`/app/download/` | `/search-mobile/`、`/nav/`、`/dh/`、`/download-app/` |
|
||||
|
||||
已移除的账户页面 `/account/**` 及旧地址 `/login/`、`/register/`、`/profile/`、`/history/`、`/favorites/`、`/purchased/`、`/setting/`、`/settings/`、`/recharge/` 的 GET/HEAD 一律 301 到首页 `/`,规则为 `src/lib/routes.ts` 的 `REMOVED_ACCOUNT_PATHS`。
|
||||
|
||||
分页和筛选保留原参数结构;搜索结果、标签、关于、帮助和法律声明沿用原路径。业务页面组件使用 `HomePage`、`BrowsePages`、`DiscoveryPages`、`DramaWatchPage`、`SeriesWatchPage`、`SiteNavigationPage`,服务端播放模块为 `src/server/pages/playback.ts`。公共组件封装、源码 CSS 的 TSX 导入和类名保持。
|
||||
|
||||
`src/app/` 页面目录按上表同名迁移,原动态参数目录保留;`nav`/`dh` 合并到同一个入口。其他代码文件的新旧对应如下:
|
||||
|
||||
| 所在目录 | 原文件 | 当前文件 |
|
||||
| --- | --- | --- |
|
||||
| `src/features/catalog/` | `IndexPage.tsx` | `HomePage.tsx` |
|
||||
| `src/features/catalog/` | `ListingPages.tsx` | `BrowsePages.tsx` |
|
||||
| `src/features/catalog/` | `SearchPages.tsx` | `DiscoveryPages.tsx` |
|
||||
| `src/features/playback/` | `PlayPage.tsx` | `DramaWatchPage.tsx` |
|
||||
| `src/features/playback/` | `SeriesPlayPage.tsx` | `SeriesWatchPage.tsx` |
|
||||
| `src/features/content/` | `NavigationPage.tsx` | `SiteNavigationPage.tsx` |
|
||||
| `src/server/pages/` | `play.ts` | `playback.ts` |
|
||||
|
||||
后台 `hkdj-web-admin` 的导航分类 URL 支持搜索、选择当前固定页面入口及直接输入具体路径或外链;历史自定义值保留,提交仍使用原 `urlValue` 字符串。完整清单源自实际路由注册,执行 `npm run routes:export -- --admin ../hkdj-web-admin` 同步本仓库 `src/lib/page-route-catalog.json`、下方对照和后台 `src/constants/hkdj-web-routes.json`;使用 `npm run routes:check -- --admin ../hkdj-web-admin` 验证一致性。动态详情、筛选和分页需要真实参数,因此只列入对照,不将未填写参数的模板作为后台可选链接。
|
||||
|
||||
本次路径更新涉及 Web、后台管理端和 Go 后端三个仓库。部署后端并由原 sitemap 任务重新生成 XML 后,文件内链接才会更新;已有 XML 仍按原字节返回,其中的旧页面地址通过 301 兼容。
|
||||
|
||||
<!-- BEGIN PAGE_ROUTE_CATALOG -->
|
||||
以下清单由 `npm run routes:export` 从实际路由注册生成,包含全部页面、筛选和分页模式。含 `:参数` 的路径需要填写真实值;后台导航下拉只展示可直接访问的固定入口,详情页和外链可手动输入。
|
||||
|
||||
| 页面 | 原路径 | 当前路径 |
|
||||
| --- | --- | --- |
|
||||
| 首页 | `/` | `/` |
|
||||
| 移动端搜索 | `/search-mobile/` | `/discover/search/` |
|
||||
| 搜索结果 | `/search/` | `/search/` |
|
||||
| 搜索结果 | `/search/:q/` | `/search/:q/` |
|
||||
| 搜索结果 | `/search/:q/page/:page/` | `/search/:q/page/:page/` |
|
||||
| 关于我们 | `/about/` | `/about/` |
|
||||
| 帮助中心 | `/help/` | `/help/` |
|
||||
| 服务条款 | `/terms/` | `/terms/` |
|
||||
| 隐私政策 | `/privacy/` | `/privacy/` |
|
||||
| 2257 声明 | `/2257/` | `/2257/` |
|
||||
| 版权声明 | `/dmca/` | `/dmca/` |
|
||||
| 下载 App | `/download-app/` | `/app/download/` |
|
||||
| 回家的路 | `/home-road/` | `/return-home/` |
|
||||
| 官方导航 | `/nav/`、`/dh/` | `/navigation/` |
|
||||
| 短剧列表 | `/list/` | `/dramas/` |
|
||||
| 短剧列表 | `/list/:theme/` | `/dramas/:theme/` |
|
||||
| 短剧列表 | `/list/:theme/:updateStatus/:sortType/` | `/dramas/:theme/:updateStatus/:sortType/` |
|
||||
| 短剧列表 | `/list/:theme/:updateStatus/:sortType/page/:page/` | `/dramas/:theme/:updateStatus/:sortType/page/:page/` |
|
||||
| 短剧推荐栏目 | `/more-list/:configId/:timeRange/:theme/page/:page/` | `/curated/dramas/:configId/:timeRange/:theme/page/:page/` |
|
||||
| 短剧推荐栏目 | `/more-list/:configId/:timeRange/:theme/` | `/curated/dramas/:configId/:timeRange/:theme/` |
|
||||
| 短剧推荐栏目 | `/more-list/:configId/page/:page/` | `/curated/dramas/:configId/page/:page/` |
|
||||
| 短剧推荐栏目 | `/more-list/:configId/` | `/curated/dramas/:configId/` |
|
||||
| 剧集推荐栏目 | `/more-series-list/:configId/:timeRange/:theme/page/:page/` | `/curated/series/:configId/:timeRange/:theme/page/:page/` |
|
||||
| 剧集推荐栏目 | `/more-series-list/:configId/:timeRange/:theme/` | `/curated/series/:configId/:timeRange/:theme/` |
|
||||
| 剧集推荐栏目 | `/more-series-list/:configId/page/:page/` | `/curated/series/:configId/page/:page/` |
|
||||
| 剧集推荐栏目 | `/more-series-list/:configId/` | `/curated/series/:configId/` |
|
||||
| 剧集列表 | `/series/` | `/series/` |
|
||||
| 剧集列表 | `/series/:media/:topic/:sortType/` | `/series/:media/:topic/:sortType/` |
|
||||
| 剧集列表 | `/series/:media/:topic/:sortType/page/:page/` | `/series/:media/:topic/:sortType/page/:page/` |
|
||||
| 专题列表 | `/topic/` | `/collections/` |
|
||||
| 专题列表 | `/topic/page/:page/` | `/collections/page/:page/` |
|
||||
| 专题列表 | `/topic/sort/:sortType/` | `/collections/sort/:sortType/` |
|
||||
| 专题列表 | `/topic/sort/:sortType/page/:page/` | `/collections/sort/:sortType/page/:page/` |
|
||||
| 专题详情 | `/topic-detail/:slug/sort/:sortType/page/:page/` | `/collection/:slug/sort/:sortType/page/:page/` |
|
||||
| 专题详情 | `/topic-detail/:slug/sort/:sortType/` | `/collection/:slug/sort/:sortType/` |
|
||||
| 专题详情 | `/topic-detail/:slug/page/:page/` | `/collection/:slug/page/:page/` |
|
||||
| 专题详情 | `/topic-detail/:slug/` | `/collection/:slug/` |
|
||||
| 排行榜 | `/rank/` | `/rankings/` |
|
||||
| 排行榜 | `/rank/page/:page/` | `/rankings/page/:page/` |
|
||||
| 排行榜 | `/rank/:sortOrder/` | `/rankings/:sortOrder/` |
|
||||
| 排行榜 | `/rank/:sortOrder/page/:page/` | `/rankings/:sortOrder/page/:page/` |
|
||||
| 标签详情 | `/tags/:slug/sort/:sortType/page/:page/` | `/tags/:slug/sort/:sortType/page/:page/` |
|
||||
| 标签详情 | `/tags/:slug/sort/:sortType/` | `/tags/:slug/sort/:sortType/` |
|
||||
| 标签详情 | `/tags/:slug/page/:page/` | `/tags/:slug/page/:page/` |
|
||||
| 标签详情 | `/tags/:slug/` | `/tags/:slug/` |
|
||||
| 最新更新 | `/recently-added/` | `/latest/` |
|
||||
| 最新更新 | `/recently-added/page/:page/` | `/latest/page/:page/` |
|
||||
| 热门推荐 | `/popular-recommend/` | `/featured/` |
|
||||
| 热门推荐 | `/popular-recommend/page/:page/` | `/featured/page/:page/` |
|
||||
| 热门推荐 | `/popular-recommend/:rankType/` | `/featured/:rankType/` |
|
||||
| 热门推荐 | `/popular-recommend/:rankType/page/:page/` | `/featured/:rankType/page/:page/` |
|
||||
| 短剧播放 | `/play/:slug/` | `/drama/:slug/` |
|
||||
| 短剧播放 | `/play/:slug/:ep/` | `/drama/:slug/:ep/` |
|
||||
| 短剧播放 | `/play/:slug/recommendations/page/:guessPage/` | `/drama/:slug/recommendations/page/:guessPage/` |
|
||||
| 短剧播放 | `/play/:slug/:ep/recommendations/page/:guessPage/` | `/drama/:slug/:ep/recommendations/page/:guessPage/` |
|
||||
| 剧集播放 | `/series-play/:slug/` | `/series/watch/:slug/` |
|
||||
| 剧集播放 | `/series-play/:slug/recommendations/page/:guessPage/` | `/series/watch/:slug/recommendations/page/:guessPage/` |
|
||||
| 意见反馈 | `/feedback/` | `/feedback/` |
|
||||
|
||||
<!-- END PAGE_ROUTE_CATALOG -->
|
||||
|
||||
SSR 在 Proxy 中完成上游数据、游客恢复和 Cookie 写回,再交给 React 渲染,避免首屏流式响应开始后丢失 Cookie。小数据使用压缩内部请求头,较大数据暂存于操作系统临时目录下的私有目录,绑定 URL/Cookie、权限 0700/0600、60 秒过期。适用于当前单 Node 实例或同一文件系统的 standalone 部署;若未来改为分布式 Proxy 与渲染节点,需要更换交接存储。
|
||||
|
||||
公共配置可短期复用;登录用户内容仅在单次请求内复用,页面响应使用 `private, no-store`。必要内容接口临时不可用返回 503、`Retry-After` 和 noindex;真实缺失的实体及越界分页仍返回 404。
|
||||
|
||||
播放器通过 `package.json` 管理 `xgplayer`、`xgplayer-hls`,按需加载官方模块和样式,支持 MP4/HLS、加密密钥、预告切换、章节播放、全屏、解锁、评论及播放埋点。原 DPlayer、全局 Hls 和浏览器业务 JS 已删除。加密图片、二维码分别封装为 TSX 组件,二维码使用 npm `qrcode`;占位图 Logo 透明度保持 0.2。
|
||||
|
||||
## SEO 保留范围
|
||||
|
||||
- 上游站点/导航/栏目/内容 SEO、筛选维度和分页独立 TDK。
|
||||
- 原收录白名单、私有页面与章节 noindex、无效实体和越界分页真实 404。
|
||||
- 永久域名优先,代理协议/Host 回退;slug canonical、尾斜杠、去查询参数。
|
||||
- OG/Twitter、Google/Yandex/Bing 验证、可配置百度验证和 IndexNow 文件。
|
||||
- Organization、WebSite、WebPage、Breadcrumb、ItemList、FAQ、TVSeries/TVEpisode、VideoObject 等原 JSON-LD 条件和真实元数据。
|
||||
- robots.txt、llms.txt、sitemap 索引/分片、纯渠道首页 302、下载地址 302。
|
||||
|
||||
上游 TDK、导航、公告和 JSON-LD 中的完整旧品牌词「黄果短剧」统一显示为「花魁短剧」。永久域名、canonical、真实联系地址、普通影片名称、验证值和 SDK 配置保留原值。
|
||||
|
||||
抓取文件遵循原 Go 路由。普通站点的 robots/llms 读取公共配置,保留 60 秒成功缓存;刷新失败沿用最近成功配置,首次失败使用空配置。地址使用原永久域名优先、代理协议/Host 回退规则。robots 按顺序读取 `template/pages/robots.txt`、`templates/pages/robots.txt`、`app/template/pages/robots.txt`、`app/templates/pages/robots.txt`,没有可用文件时使用原内嵌文本,再回退原内置默认文本。保留原爬虫规则、行处理、已有 Host 和大小写语义,按原算法写入 Sitemap 地址。
|
||||
|
||||
**sitemap 由原后端生成,Next 只提供文件服务。** 开发环境的 `SITEMAP_DIR` 默认 `./template/pages/sitemaps`;测试和生产默认 `../template/pages/sitemaps`,均相对 Node 工作目录解析。测试 Node 运行于 `/server/hkdj_net/h5/next`,后端持续写入 `/server/hkdj_net/h5/template/pages/sitemaps`;正式环境同样按 `h5/next` 与 `h5/template/pages/sitemaps` 的父子目录关系读取,目录不同可通过对应 `.local.env` 或进程变量覆盖。`/sitemap.xml` 先读取该目录的 `sitemap.xml`,再读取 `./template/pages/sitemap.xml`;`/sitemaps/:name` 仅读取该目录中通过原文件名校验的分片。成功响应保留原 XML 字节、loc、priority、lastmod 和文件响应语义,不改域名、不重写新旧路径,也不从分片生成索引。缺失索引、非法或缺失分片均走原站完整 404 页面及其共享数据加载,不生成静态页面 sitemap、空 XML 或远程 API 兜底。
|
||||
|
||||
成功的抓取文件通过原生 Route Handler 输出,保留 `text/xml; charset=utf-8`、Last-Modified、Content-Length、Accept-Ranges、条件请求和单段/多段 Range;HEAD 不返回 body。Proxy 提前将缺失文件、非法分片和不支持的方法交给完整 TSX 404;导航模式按 Go `DHInit` 不开放这些路由。Next 应用压缩关闭以保持原 Go 文件字节长度与 Range 语义,公网代理可沿用原有传输压缩配置。
|
||||
|
||||
新旧服务应读取同一份持续更新的后端产物;文件来源目录不要放进前端发布时会清空的构建目录。也可通过 `SITEMAP_DIR=/绝对路径/template/pages/sitemaps` 指向后端目录,或使用保留该关系的目录挂载。公网代理仍需正确转发 `Host`、`X-Forwarded-Host` 与 `X-Forwarded-Proto`;不提供额外公网 origin 开关或 IP 限制来改变原站地址规则。
|
||||
|
||||
本地需要真实 XML 时,显式执行只读同步命令;页面请求、开发启动和构建不会连接 SSH:
|
||||
|
||||
```bash
|
||||
npm run sitemap:sync
|
||||
```
|
||||
|
||||
该命令始终按开发配置读取,优先级为进程变量 > `config/development.local.env` > `config/development.env`;已设置 `APP_ENV=test/production` 时拒绝执行,避免继承服务端目录。开发默认 SSH 来源为 `root@172.104.181.99:22889` 的 `/server/hkdj_net/h5/template/pages/sitemaps`,可覆盖 `SITEMAP_SYNC_HOST`、`SITEMAP_SYNC_PORT`、`SITEMAP_SYNC_DIR` 与本地 `SITEMAP_DIR`。需提前配置 SSH 密钥及可信 known_hosts;命令不交互输入密码,也不关闭主机校验。仅下载原索引与其引用的有效分片,校验 XML 和索引稳定性后保留原字节、域名、路径和修改时间落盘,最后替换索引;不生成 sitemap、不写远端、不删除未引用的本地文件。同步 XML 及旧本地 `data/sitemaps` 兼容文件均忽略提交;页面 trace 排除 XML,最终发布包还会定向清理 Proxy trace 带入的副本。
|
||||
|
||||
`SEO_INDEXNOW_KEY` 在三套环境配置中使用同一份重新生成的 Key,并与代码默认值一致;站点根路径 `/{key}.txt` 自动返回该 Key。后端推送进程的 `HKDJNET_INDEXNOW_KEY` 需使用相同值。百度可通过 `SEO_BAIDU_SITE_VERIFICATION`、`SEO_BAIDU_HTML_FILE` 配置,验证文件放入 `config/seo/`。搜索引擎实际收录取决于真实域名、后端内容和站长平台状态。
|
||||
|
||||
## Figma 与界面资源
|
||||
|
||||
设计来源:[花魁短剧 WEB / H5](https://www.figma.com/design/1vUq4wf5xOs8ZOlmt26Q3O/?node-id=0-1)。页头、页尾、徽标、favicon 和匹配图标均通过 Figma REST API 导出;资源保存在本地,运行和构建不依赖 Figma。SVG 保存 REST 返回的完整原始字节与 Figma 原始尺寸,显示尺寸由 CSS 或 HTML 的宽高属性决定。导出节点、尺寸和校验信息见 `public/static/brand/manifest.json` 与 `scripts/figma-icon-manifest.json`。没有精确对应节点的原有功能图标保留,清单记录原因。
|
||||
|
||||
导航、登录及 SEO 使用 `/static/brand/huakui-20261013/` 下的版本化品牌资源,路径统一在 `src/lib/brand-assets.ts` 管理,避免旧站同名静态资源和 CDN 缓存继续显示旧 logo。`assets:sync` 同步生成这些资源并写入校验清单;原路径保留兼容,PWA 图标也从新版品牌源图生成。
|
||||
|
||||
```bash
|
||||
# 仅维护设计资源时需要本机 FIGMA_TOKEN;不将 token 写入仓库。
|
||||
npm run assets:sync
|
||||
# 离线检查资源完整性,不访问 Figma。
|
||||
npm run assets:check
|
||||
```
|
||||
|
||||
首页、列表、播放器和静态页面分别适配 1440px 桌面与 375px 手机设计。H5 底部导航按设计页面显示。影片、广告、余额、套餐、社交地址和二维码继续使用真实上游配置,不将设计示例数据当作业务内容。
|
||||
|
||||
设置页的头像、昵称、简介、隐私模式和密码使用现有上传与资料接口。通知偏好、邮箱修改及注销没有对应上游能力,保留设计位置并明确禁用,避免显示保存成功却未生效。
|
||||
|
||||
下载入口共用桌面下载弹窗与 H5 安装引导;iOS 支持 App Store、轻量版两个页签,Android 展示安装步骤。以下服务端变量可分别配置真实安装地址,三套环境默认均留空:
|
||||
|
||||
```dotenv
|
||||
APP_IOS_STORE_URL=
|
||||
APP_IOS_PROFILE_URL=
|
||||
APP_ANDROID_APK_URL=
|
||||
APP_DEEP_LINK_URL=
|
||||
```
|
||||
|
||||
平台地址未配置时,按钮显示“前往下载页”,沿用上游 `AppAddr` 或 `/app/download/` 跳转;未配置 App 深链时隐藏打开 App 入口。链接保留原始跳转地址与导航埋点,不生成设计示例中的安装包、描述文件或 App Store 地址。
|
||||
|
||||
## 接口和媒体
|
||||
|
||||
保留原 BFF 路由、别名、参数校验、业务响应壳、上传和 CSV 导出。`Cookie`、cookie token 优先的 `Authorization`、客户端 IP、浏览器 UA、`X-User-Agent`、`X-SDK-Common` 透传;过滤 hop-by-hop 和内部控制头。多个 `Set-Cookie` 独立回传。埋点上下文按原白名单校验并保留 SDK 原字段。
|
||||
|
||||
公共配置隔离过期登录凭证;个人资料刷新与游客恢复写回当前请求,避免首屏重复建立会话。正式账号失效和内容允许降级游客的规则分别保留。
|
||||
|
||||
`/h5/m3u8/*`、`/enkey`、`/newenkey` 保留查询签名、密钥和媒体响应,媒体请求不强制携带登录凭证。`/enkey` 上游不可用时支持 `M3U8_KEY_FILE` 本地兜底;原本地密钥已复制到忽略提交的 `config/secrets/m3u8.key`。部署时提供同一文件或挂载其路径,文件不要放入 `public`。
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
npm run check
|
||||
npm run assets:check
|
||||
npm run build:dev
|
||||
npm run build:test
|
||||
npm run build
|
||||
npm run test:catalog
|
||||
npm run test:popups
|
||||
npm run test:playback
|
||||
npm run test:ssr
|
||||
SMOKE_URL=http://localhost:25131 npm run test:browser
|
||||
SMOKE_URL=http://localhost:25131 npm run test:ui
|
||||
```
|
||||
|
||||
浏览器检查默认使用 macOS Chrome,可通过 `CHROME_PATH` 指定。`test:catalog`、`test:popups`、`test:playback` 使用隔离上游与真实 React 组件,验证目录交互、弹层焦点/滚动、官方 xgplayer、加密 HLS、评论与埋点,不改动真实账号。`test:ssr` 使用生产构建核验 SEO、HTTP 状态、Cookie、透传和并发隔离,不访问真实业务接口。
|
||||
|
||||
全站界面截图默认输出 `/tmp/hkdj-figma/implementation`,浏览器基础检查输出 `/tmp/hkdj-browser-smoke`。完整真实内容回归需要原上游剧集、专题与标签数据;空环境可设置 `UI_ALLOW_EMPTY_CONTENT=1`,缺失实体仍必须返回 404 与 noindex。
|
||||
|
||||
原 Go 输出夹具保留为 SEO、链接、卡片、预览/可播/锁定场景的迁移参照。签名时间、票据和后端实时热度属于动态值。Next.js 会先规范化连续斜杠和 `..` 路径,此类异常 URL 与 Gin 可能存在差异,正常站内链接、canonical 和原筛选/分页规则保留。
|
||||
|
||||
## 独立部署
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm run bundle
|
||||
cd dist/production
|
||||
UPSTREAM_API_URL=https://your-api.example node start.mjs
|
||||
```
|
||||
|
||||
`bundle` 包含 standalone 服务、完整静态资源、对应环境公共配置,以及 robots 和站点验证文件,无需在部署机器安装依赖。后端生成的 sitemap XML(包括开发同步副本)不打入前端包;发布程序必须保留并挂载服务器持续更新的源目录。生产机可以通过进程变量或 `config/production.local.env` 提供配置;sitemap 和密钥可外部挂载。`SITEMAP_DIR` 指向的外部目录不会被构建或打包扫描、生成或覆盖。测试包使用 `npm run bundle:test`。不要在构建机器本地覆盖文件中放入需要打包的生产凭证。
|
||||
|
||||
Next 16.4 当前仍可能通过 Node Proxy 的 `middleware.js.nft.json` 将开发 XML 复制到原始 `.next*/standalone`,页面级 tracing 排除不能保证该目录无 XML。发布应使用 `npm run bundle` / `bundle:test` 生成的 `dist`:打包脚本只在复制后的独立目录清理 `template/pages/sitemaps`、`data/sitemaps` 内的合法 sitemap 文件及备用 `template/pages/sitemap.xml`。清理前逐层检查父目录,不接受符号链接或异常文件;只 unlink 已确认的文件,不递归删目录,也不跟随后端挂载。robots、密钥及未知文件保留。
|
||||
|
||||
也提供 Dockerfile,构建参数 `APP_ENV=production|test|development` 选择环境,启动时传入 `UPSTREAM_API_URL`,容器默认端口 3000。
|
||||
|
||||
## 测试服上传
|
||||
|
||||
参考 `naixin-web-nuxt` 的直连测试发布流程,入口为仓库根目录 `deploy_test.sh`:
|
||||
|
||||
```bash
|
||||
# 只核对流程和目标,不构建、不连接服务器
|
||||
DEPLOY_DRY_RUN=1 npm run deploy:test
|
||||
|
||||
# 检查、构建、打包、上传并更新测试服
|
||||
npm run deploy:test
|
||||
# 等价于 ./deploy_test.sh
|
||||
```
|
||||
|
||||
默认目标为 `root@172.104.181.99`,SSH 端口 `22889`,上传目录 `/server/hkdj_net/h5/`。可通过 `DEPLOY_HOST`、`DEPLOY_SSH_PORT`、`DEPLOY_DIR` 临时覆盖。发布后默认检查公网 `https://hkdjneth5.remoces.com`,可通过 `DEPLOY_CHECK_URL` 覆盖为另一个 HTTP(S) 站点根地址;dry-run 仅显示该地址,不访问域名。需要本机已安装依赖、zip/unzip、SSH/SCP,并提供忽略提交的 `config/secrets/m3u8.key`。
|
||||
|
||||
脚本依次执行 `npm run check`、`build:test`、`bundle:test`,生成 `dist.zip` 和 `dist.zip.sha256`,连同 `deploy/up_next_h5.sh` 上传。包保留配置、页面和静态资源,排除 `node_modules` 与本地环境覆盖文件;服务器在暂存目录按 lockfile 执行 `npm ci --omit=dev --include=optional --ignore-scripts`,为 Linux 安装对应架构的运行依赖。服务器需要 Node.js **20.9+**(推荐 22)、npm、可访问 npm 包源,以及 Bash、unzip、sha256sum、curl 和 Supervisor。运行时 `dotenv` 属于正式依赖。
|
||||
|
||||
服务器脚本校验包后更新 `/server/hkdj_net/h5/next/`,上一版留在 `next.prev/`,通过独立 Supervisor 程序 `hkdj_next_h5` 运行 `node start.mjs`。保留服务器的 `next/config/test.local.env`、密钥和后端生成的 sitemap:运行目录内默认或自定义目录中的原 XML 优先于包内文件,第二索引 `template/pages/sitemap.xml` 和四个可选 robots 覆盖文件同样保留;新目录不存在时可迁入旧版本 `data/sitemaps`,已有空目录也视为权威,不恢复旧 XML。已配置的外置目录不复制、不清空、不覆盖,默认目录的符号链接及其运行目录内目标也保留。首次部署可读取原 Go 目录的 XML;后台持续写原目录时应配置外置 `SITEMAP_DIR`,避免只使用一次复制的快照。密钥首次可复用原目录 `config/secrets/m3u8.key` 或 `conf/m3u8.key`。后续私密配置放服务器的 `next/config/test.local.env`,不要放入上传包。启动与健康检查失败会返回非零状态,切换后的失败会尝试恢复上一版。本地包及失败时的服务器上传包留存供排查。
|
||||
|
||||
**测试站 Nginx 必须代理到 `127.0.0.1:30334`**(若服务器覆盖 `PORT`,使用实际端口),包括页面、`/_next/`、`/static/`、`/api/` 和媒体路径;上传脚本不修改 Nginx 配置。此前 `/etc/nginx/conf.d/hkdj_net-h5.conf` 将该域名代理到 API 端口 `30331`,导致首页和 `/healthz` 都返回 `{"code":1000,"msg":""}`,本机服务健康也无法证明域名已切流。
|
||||
|
||||
服务器健康检查覆盖 `/healthz`、首页及 `/enkey`;随后本地脚本通过公网域名检查 `/healthz` 必须为 HTTP 200 且 JSON `status=ok`、`environment=test`,首页必须为 HTTP 200、`text/html` 并含 Next 静态资源标记。公网检查最多重试 3 次,每次请求超时 4 秒,失败会明确提示“服务器服务已更新,但域名检查失败”,返回非零状态;检查不通过不会误报完整发布成功,也不会自动回滚已经健康的新服务。完成后仍应通过测试站域名检查 PC/H5 页面和真实媒体链路。
|
||||
|
||||
## 正式服上传
|
||||
|
||||
使用既有跳板机 `running.sh` 链路:
|
||||
|
||||
```bash
|
||||
npm run deploy:prod -- --dry-run # 只核对配置,不构建、不连接
|
||||
npm run deploy:prod -- --package-only # 检查、构建、生成运行包与 MD5,不上传
|
||||
npm run deploy:prod # 构建、上传并执行正式服 running.sh
|
||||
```
|
||||
|
||||
脚本执行 `check` → `build` → `bundle` → `bundle:linux`,生成根目录 `web_hkdj_h5.tar.gz` 和 `web_hkdj_h5.md5`(仅一行 MD5 hash)。tar 根部直接包含 `start.mjs`、`server.js`、`.next/`(含静态资源)、`public/`(含 PWA Worker/图标)、`config/production.env`、媒体密钥、必要数据及 `node_modules/`,不套 `dist/production/` 外层目录。
|
||||
|
||||
本机最低 Node.js 20.9、npm **10.9.2**,推荐 Node.js 22。正式包保留 Next standalone 已跟踪并裁剪的 `node_modules`;只在临时目录安装与 Linux 目标匹配的 Sharp/libvips 两个原生包,以替换 macOS 原生文件,版本、下载地址和 integrity 均取仓库 lockfile。最终输出为 `dist/production-linux-x64-glibc/`,包含裁剪后的 Next/React/Sharp 运行文件、`dotenv`、`@swc/helpers` 和已跟踪的 WASM 回退,不携带完整 Next 安装树、SWC 原生编译器、Playwright 或未跟踪的客户端依赖。包内无需执行 `npm install`,直接 `node start.mjs`;临时安装树不会进入发布包。
|
||||
|
||||
默认目标为 Linux **x64/glibc**;可设置 `DEPLOY_TARGET_ARCH=x64|arm64`、`DEPLOY_TARGET_LIBC=glibc|musl`。仓库 package/lock、本机依赖及 `dist/production/` 保持独立。运行包排除 `.env*`、`*.local.env`、日志及 macOS 元数据,密钥随包提供;生产私密覆盖配置需在服务器提供,并由服务器发布脚本保留。
|
||||
|
||||
默认上传及触发命令严格对应两种端口约定:
|
||||
|
||||
```bash
|
||||
scp -P 22889 web_hkdj_h5.tar.gz web_hkdj_h5.md5 \
|
||||
server@116.212.120.2:/home/server/web_hkdj/h5/nuxt/
|
||||
ssh -p 22 server@116.212.120.2 /home/server/web_hkdj/h5/nuxt/running.sh
|
||||
```
|
||||
|
||||
可通过 `DEPLOY_JUMP_HOST`、`DEPLOY_JUMP_DIR`、`DEPLOY_SCP_PORT`、`DEPLOY_SSH_PORT` 覆盖连接参数。跳板机脚本只能按完整路径执行,不带参数或额外 shell 命令;脚本和正式机 `up_node_h5.sh` 由既有部署链路管理。同仓库使用发布锁防止并发构建/上传覆盖同名包,构建后显示压缩包体积。任何构建、上传或远端执行失败均返回非零,本地包留存;正式脚本输出保存到 `dist/production-running.*`,即使远端返回 0,出现 `BACKOFF`、`FATAL`、`ERROR` 或 `spawn error` 也会判定失败。端口和健康检查由服务器发布脚本负责。正式站 Nginx 应将页面及 Next/BFF/媒体资源代理至 `26935`,公网域名需在发布后另行验证。
|
||||
|
||||
正式机 `up_node_h5.sh` 必须完整复制隐藏目录,例如 `cp -rf "$STAGING/." "$APP_DIR/"`;使用 `$STAGING/*` 会漏掉必要的 `.next/`。覆盖前保留 `.local.env`、媒体密钥、`template/pages/sitemaps` 和 `template/pages/sitemap.xml`,服务器已有 XML 优先于包内同名文件;外置 `SITEMAP_DIR` 或挂载目录不得随版本目录删除。复制后应检查 `.next/BUILD_ID` 和 `start.mjs`,启动一次并检查 Supervisor 状态,复制或启动失败须返回非零状态。该远端脚本由既有链路管理,不包含在本仓库。
|
||||
|
||||
正式 Next 运行目录使用 `/server/web_hkdj/h5/next/`,Supervisor 的 `directory` 与更新脚本的 `APP_DIR` 必须一致;更新脚本先创建目录,再检查 `cd` 是否成功。不要直接运行在旧 Go 的目录里:Next 会优先读取根部遗留的 `static/`,遮盖新包的 `public/static/`。`start.mjs` 检测到这种混用会拒绝启动;迁移时保留服务器的本地环境覆盖和 sitemap 数据。
|
||||
@@ -0,0 +1,26 @@
|
||||
APP_ENV=development
|
||||
UPSTREAM_API_URL=http://172.104.181.99:30331
|
||||
# UPSTREAM_API_URL=http://172.105.118.202:26931
|
||||
# The development API returns relative image keys and has no imgCdn configured.
|
||||
# Verified image source from the original site's imgAppUsCdn; API stays on 30331.
|
||||
# Primary public domain; overrides the backend foreverUrlAddr.
|
||||
SITE_URL=https://huakuidj.com
|
||||
IMAGE_CDN_URL=https://d3n8qx4rvy4vol.cloudfront.net/
|
||||
UPSTREAM_TIMEOUT_MS=20000
|
||||
TRACKER_APP_ID=JHA-4001
|
||||
TRACKER_ENCRYPTED_CONFIG=
|
||||
PORT=25121
|
||||
# Backend-generated sitemap files; relative to the Node working directory.
|
||||
SITEMAP_DIR=./template/pages/sitemaps
|
||||
# Explicit development-only SSH pull; request handlers never contact this host.
|
||||
SITEMAP_SYNC_HOST=root@172.104.181.99
|
||||
SITEMAP_SYNC_PORT=22889
|
||||
SITEMAP_SYNC_DIR=/server/hkdj_net/h5/template/pages/sitemaps
|
||||
M3U8_KEY_FILE=./config/secrets/m3u8.key
|
||||
SEO_INDEXNOW_KEY=d8e5b742d13809c10865fc3b29de24ee
|
||||
SEO_BAIDU_SITE_VERIFICATION=
|
||||
SEO_BAIDU_HTML_FILE=
|
||||
APP_IOS_STORE_URL=
|
||||
APP_IOS_PROFILE_URL=
|
||||
APP_ANDROID_APK_URL=
|
||||
APP_DEEP_LINK_URL=
|
||||
@@ -0,0 +1,21 @@
|
||||
APP_ENV=production
|
||||
# Set your production API in config/production.local.env or deployment environment.
|
||||
UPSTREAM_API_URL=http://127.0.0.1:26931
|
||||
# Optional fallback when config/info does not supply an image CDN.
|
||||
# Primary public domain; overrides the backend foreverUrlAddr.
|
||||
SITE_URL=https://huakuidj.com
|
||||
IMAGE_CDN_URL=https://d3n8qx4rvy4vol.cloudfront.net/
|
||||
UPSTREAM_TIMEOUT_MS=20000
|
||||
TRACKER_APP_ID=JHA-4001
|
||||
TRACKER_ENCRYPTED_CONFIG=
|
||||
PORT=26935
|
||||
# Node runs in h5/next; the backend generates files in h5/template/pages/sitemaps.
|
||||
SITEMAP_DIR=../template/pages/sitemaps
|
||||
M3U8_KEY_FILE=./config/secrets/m3u8.key
|
||||
SEO_INDEXNOW_KEY=d8e5b742d13809c10865fc3b29de24ee
|
||||
SEO_BAIDU_SITE_VERIFICATION=
|
||||
SEO_BAIDU_HTML_FILE=
|
||||
APP_IOS_STORE_URL=
|
||||
APP_IOS_PROFILE_URL=
|
||||
APP_ANDROID_APK_URL=
|
||||
APP_DEEP_LINK_URL=
|
||||
@@ -0,0 +1,21 @@
|
||||
APP_ENV=test
|
||||
# Test API runs on the same server, using the development API port.
|
||||
UPSTREAM_API_URL=http://127.0.0.1:30331
|
||||
# The API on 30331 does not supply an image CDN.
|
||||
# Primary public domain; overrides the backend foreverUrlAddr.
|
||||
SITE_URL=https://huakuidj.com
|
||||
IMAGE_CDN_URL=https://d3n8qx4rvy4vol.cloudfront.net/
|
||||
UPSTREAM_TIMEOUT_MS=20000
|
||||
TRACKER_APP_ID=JHA-4001
|
||||
TRACKER_ENCRYPTED_CONFIG=
|
||||
PORT=30334
|
||||
# Node runs in h5/next; the backend generates files in h5/template/pages/sitemaps.
|
||||
SITEMAP_DIR=../template/pages/sitemaps
|
||||
M3U8_KEY_FILE=./config/secrets/m3u8.key
|
||||
SEO_INDEXNOW_KEY=d8e5b742d13809c10865fc3b29de24ee
|
||||
SEO_BAIDU_SITE_VERIFICATION=
|
||||
SEO_BAIDU_HTML_FILE=
|
||||
APP_IOS_STORE_URL=
|
||||
APP_IOS_PROFILE_URL=
|
||||
APP_ANDROID_APK_URL=
|
||||
APP_DEEP_LINK_URL=
|
||||
@@ -0,0 +1,296 @@
|
||||
#!/usr/bin/env bash
|
||||
# 测试服专用:dist.zip 为 dist/test 内容(不含 node_modules),与校验文件放在本脚本旁。
|
||||
# 安装到 next/,保留原 Go 文件;仅管理独立的 hkdj_next_h5 Supervisor 程序。
|
||||
set -Eeuo pipefail
|
||||
|
||||
DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
PACKAGE="$DIR/dist.zip"
|
||||
CHECKSUM="$DIR/dist.zip.sha256"
|
||||
CURRENT="$DIR/next"
|
||||
PREVIOUS="$DIR/next.prev"
|
||||
PROGRAM=hkdj_next_h5
|
||||
SUP_CONF_DIR=${SUP_CONF_DIR:-/etc/supervisor/conf.d}
|
||||
SUP_LOG_DIR=${SUP_LOG_DIR:-/var/log/supervisor}
|
||||
CONF="$SUP_CONF_DIR/$PROGRAM.conf"
|
||||
LOCK="$DIR/.$PROGRAM.lock"
|
||||
STAMP=$(date +%Y%m%d%H%M%S)-$$
|
||||
STAGING= UPLOAD_COPY= HEALTH_FILE= CONF_BACKUP= NEW_CONF=
|
||||
LOCKED=0 SWITCHED=0 OLD_MOVED=0 INSTALLED=0 HAD_CONF=0 CONF_CHANGED=0 WAS_RUNNING=0
|
||||
|
||||
fail() { printf '发布失败:%s\n' "$*" >&2; exit 1; }
|
||||
rollback() {
|
||||
echo '检查失败,恢复上一版应用与 Supervisor 配置。' >&2
|
||||
supervisorctl stop "$PROGRAM" >/dev/null 2>&1 || true
|
||||
if [ "$INSTALLED" = 1 ] && [ -d "$CURRENT" ]; then
|
||||
mv "$CURRENT" "$DIR/.${PROGRAM}.failed.$STAMP" || return 1
|
||||
fi
|
||||
if [ "$OLD_MOVED" = 1 ]; then mv "$PREVIOUS" "$CURRENT" || return 1; fi
|
||||
if [ "$CONF_CHANGED" = 1 ]; then
|
||||
if [ "$HAD_CONF" = 1 ]; then cp -p "$CONF_BACKUP" "$CONF" || return 1
|
||||
else rm -f "$CONF" || return 1; fi
|
||||
fi
|
||||
supervisorctl reread >/dev/null && supervisorctl update >/dev/null || return 1
|
||||
if [ "$WAS_RUNNING" = 1 ]; then
|
||||
supervisorctl restart "$PROGRAM" >/dev/null || return 1
|
||||
else supervisorctl stop "$PROGRAM" >/dev/null 2>&1 || true; fi
|
||||
echo '上一版已恢复;失败版本和上传包已保留。' >&2
|
||||
}
|
||||
finish() {
|
||||
local rc=$?
|
||||
trap - EXIT
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
if [ "$SWITCHED" = 1 ]; then
|
||||
rollback || {
|
||||
echo "自动回滚未完成,请检查 ${CURRENT}、${PREVIOUS} 和 ${CONF_BACKUP}。" >&2
|
||||
printf '手动恢复:supervisorctl stop %s; mv %q %q; mv %q %q\n' \
|
||||
"$PROGRAM" "$CURRENT" "$DIR/.${PROGRAM}.failed.manual.$STAMP" "$PREVIOUS" "$CURRENT" >&2
|
||||
[ -z "$CONF_BACKUP" ] || printf 'cp -p %q %q\n' "$CONF_BACKUP" "$CONF" >&2
|
||||
echo "然后 supervisorctl reread && supervisorctl update && supervisorctl start $PROGRAM" >&2
|
||||
}
|
||||
fi
|
||||
echo "发布未完成,保留 $PACKAGE 便于排查。" >&2
|
||||
fi
|
||||
[ -z "$STAGING" ] || rm -rf "$STAGING"
|
||||
[ -z "$UPLOAD_COPY" ] || rm -rf "$UPLOAD_COPY"
|
||||
[ -z "$HEALTH_FILE" ] || rm -f "$HEALTH_FILE"
|
||||
[ -z "$NEW_CONF" ] || rm -f "$NEW_CONF"
|
||||
[ "$LOCKED" = 0 ] || rmdir "$LOCK" 2>/dev/null || true
|
||||
exit "$rc"
|
||||
}
|
||||
trap finish EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
for command_name in sha256sum unzip supervisorctl curl mktemp; do
|
||||
command -v "$command_name" >/dev/null || fail "缺少 $command_name"
|
||||
done
|
||||
mkdir "$LOCK" 2>/dev/null || fail "另一次发布正在执行($LOCK);确认无发布进程后才能移除此锁"
|
||||
LOCKED=1
|
||||
[ -f "$PACKAGE" ] && [ -f "$CHECKSUM" ] || fail '缺少 dist.zip 或 dist.zip.sha256'
|
||||
# SCP 本身不持有发布锁:先冻结上传文件,后续校验与解包始终使用同一份快照。
|
||||
UPLOAD_COPY=$(mktemp -d "$DIR/.${PROGRAM}.upload.XXXXXX")
|
||||
cp "$PACKAGE" "$UPLOAD_COPY/dist.zip"
|
||||
cp "$CHECKSUM" "$UPLOAD_COPY/dist.zip.sha256"
|
||||
EXPECTED=$(awk 'NR == 1 { print tolower($1) }' "$UPLOAD_COPY/dist.zip.sha256")
|
||||
[[ "$EXPECTED" =~ ^[[:xdigit:]]{64}$ ]] || fail 'SHA-256 校验文件格式无效'
|
||||
ACTUAL=$(sha256sum "$UPLOAD_COPY/dist.zip" | awk '{print $1}')
|
||||
[ "$EXPECTED" = "$ACTUAL" ] || fail 'dist.zip SHA-256 不匹配'
|
||||
|
||||
node_ok() {
|
||||
"$1" -e 'const [a,b]=process.versions.node.split(".").map(Number); process.exit(a>20 || a===20 && b>=9 ? 0 : 1)' 2>/dev/null
|
||||
}
|
||||
candidates=("${NODE_BIN:-}" /usr/local/bin/node /usr/bin/node /opt/node/bin/node)
|
||||
for candidate in /usr/local/node*/bin/node /opt/node*/bin/node \
|
||||
/usr/local/lib/nodejs/*/bin/node "${HOME:-/root}"/.nvm/versions/node/v*/bin/node; do
|
||||
[ ! -x "$candidate" ] || candidates+=("$candidate")
|
||||
done
|
||||
candidates+=("$(command -v node || true)")
|
||||
NODE_BIN=
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if [ -x "$candidate" ] && node_ok "$candidate"; then NODE_BIN=$candidate; break; fi
|
||||
done
|
||||
[ -n "$NODE_BIN" ] || fail '需要 Node.js >=20.9,推荐 Node.js 22;可用 NODE_BIN 指定路径'
|
||||
export PATH="$(dirname "$NODE_BIN"):$PATH"
|
||||
NPM_BIN="$(dirname "$NODE_BIN")/npm"
|
||||
[ -x "$NPM_BIN" ] || NPM_BIN=$(command -v npm || true)
|
||||
[ -n "$NPM_BIN" ] && [ -x "$NPM_BIN" ] || fail "选定 Node 缺少 npm:$NODE_BIN"
|
||||
supervisorctl pid >/dev/null || fail 'Supervisor 未运行或无法连接'
|
||||
STATUS=$(supervisorctl status "$PROGRAM" 2>/dev/null || true)
|
||||
[[ "$STATUS" != *RUNNING* ]] || WAS_RUNNING=1
|
||||
[ ! -L "$CURRENT" ] && [ ! -L "$PREVIOUS" ] || fail 'next/next.prev 不允许是符号链接'
|
||||
echo "使用 Node $("$NODE_BIN" -v),在 Linux 暂存目录安装运行依赖。"
|
||||
|
||||
STAGING=$(mktemp -d "$DIR/.${PROGRAM}.staging.XXXXXX")
|
||||
# 不接受绝对路径、父目录跳转或 node_modules;运行依赖必须由目标机器安装。
|
||||
unzip -Z1 "$UPLOAD_COPY/dist.zip" | while IFS= read -r entry; do
|
||||
case "$entry" in
|
||||
/*|../*|*/../*|*/..|node_modules|node_modules/*|*/node_modules/*)
|
||||
echo "包内包含不允许的路径:$entry" >&2; exit 1 ;;
|
||||
esac
|
||||
done || fail 'ZIP 路径检查失败'
|
||||
unzip -q "$UPLOAD_COPY/dist.zip" -d "$STAGING"
|
||||
for required in start.mjs server.js package.json package-lock.json .next-test/BUILD_ID \
|
||||
config/test.env config/secrets/m3u8.key; do
|
||||
[ -s "$STAGING/$required" ] || fail "包中缺少非空文件 $required"
|
||||
done
|
||||
for required in .next-test/static public; do
|
||||
[ -d "$STAGING/$required" ] || fail "包中缺少目录 $required"
|
||||
done
|
||||
[ ! -L "$STAGING/config" ] && [ ! -L "$STAGING/node_modules" ] || fail '包内配置或依赖目录不能是符号链接'
|
||||
|
||||
# 服务器的运行配置、密钥和已生成 sitemap 优先,始终不覆盖原 Go 应用目录。
|
||||
cp "$STAGING/config/secrets/m3u8.key" "$UPLOAD_COPY/packaged-m3u8.key"
|
||||
if [ -f "$CURRENT/config/test.local.env" ]; then
|
||||
cp -p "$CURRENT/config/test.local.env" "$STAGING/config/test.local.env"
|
||||
fi
|
||||
if [ -d "$CURRENT/config/secrets" ]; then
|
||||
cp -a "$CURRENT/config/secrets/." "$STAGING/config/secrets/"
|
||||
fi
|
||||
# 空旧密钥不覆盖包中有效密钥,随后再选择服务器已有的非空密钥。
|
||||
cp "$UPLOAD_COPY/packaged-m3u8.key" "$STAGING/config/secrets/m3u8.key"
|
||||
for existing_key in "$CURRENT/config/secrets/m3u8.key" "$DIR/config/secrets/m3u8.key" "$DIR/conf/m3u8.key"; do
|
||||
if [ -s "$existing_key" ]; then cp -p "$existing_key" "$STAGING/config/secrets/m3u8.key"; break; fi
|
||||
done
|
||||
(cd "$STAGING" && "$NPM_BIN" ci --omit=dev --include=optional --ignore-scripts --no-audit --no-fund)
|
||||
# BEGIN preserve backend sitemaps
|
||||
(cd "$STAGING" && "$NODE_BIN" --input-type=module - "$CURRENT" "$STAGING" "$DIR" <<'NODE'
|
||||
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, rmSync, statSync, symlinkSync } from 'node:fs';
|
||||
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
||||
import { parse } from 'dotenv';
|
||||
const [current, staging, original] = process.argv.slice(2).map(value => resolve(value));
|
||||
const read = file => existsSync(file) ? parse(readFileSync(file)) : {};
|
||||
const config = { ...read(join(staging, 'config/test.env')), ...read(join(staging, 'config/test.local.env')), ...process.env };
|
||||
const configured = (config.SITEMAP_DIR || './template/pages/sitemaps').trim() || './template/pages/sitemaps';
|
||||
const source = resolve(current, configured), suffix = relative(current, source);
|
||||
const insideCurrent = suffix !== '..' && !suffix.startsWith('..' + '/') && !isAbsolute(suffix);
|
||||
const nameAllowed = name => name === 'sitemap.xml' || /^sitemap-[a-z0-9-]+\.xml$/.test(name);
|
||||
const info = file => { try { return lstatSync(file); } catch (error) { if (error.code === 'ENOENT') return null; throw error; } };
|
||||
const withinCurrent = target => { const part = relative(current, target); return part !== '..' && !part.startsWith('../') && !isAbsolute(part); };
|
||||
const ensureParent = destination => {
|
||||
if (destination === staging) return;
|
||||
const suffix = relative(staging, dirname(destination));
|
||||
if (suffix === '..' || suffix.startsWith('../') || isAbsolute(suffix)) throw new Error('Sitemap destination escapes staging');
|
||||
let cursor = staging;
|
||||
for (const segment of suffix.split('/').filter(Boolean)) {
|
||||
cursor = join(cursor, segment);
|
||||
if (info(cursor)?.isSymbolicLink()) throw new Error('Sitemap destination parent cannot be a symbolic link');
|
||||
mkdirSync(cursor, { recursive: true });
|
||||
}
|
||||
};
|
||||
const available = directory => {
|
||||
const value = info(directory);
|
||||
// An explicitly empty current directory is authoritative; do not revive older XML.
|
||||
return value?.isSymbolicLink() || value?.isDirectory();
|
||||
};
|
||||
const visited = new Set();
|
||||
const preserveDirectory = (existing, destination) => {
|
||||
if (visited.has(existing)) throw new Error('Sitemap mount contains a symbolic-link cycle');
|
||||
visited.add(existing);
|
||||
ensureParent(destination);
|
||||
if (info(existing)?.isSymbolicLink()) {
|
||||
const link = readlinkSync(existing), target = resolve(dirname(existing), link);
|
||||
// Keep release-local link targets as well as their link; external writers stay external.
|
||||
if (withinCurrent(target) && available(target)) preserveDirectory(target, resolve(staging, relative(current, target)));
|
||||
rmSync(destination, { recursive: true, force: true });
|
||||
symlinkSync(link, destination);
|
||||
return;
|
||||
}
|
||||
if (info(destination)?.isSymbolicLink()) throw new Error('Packaged sitemap destination cannot be a symbolic link');
|
||||
mkdirSync(destination, { recursive: true });
|
||||
// Server files, including intentional absence, take precedence over stale build copies.
|
||||
for (const name of readdirSync(destination).filter(nameAllowed)) {
|
||||
const target = join(destination, name);
|
||||
if (info(target)?.isFile() || info(target)?.isSymbolicLink()) rmSync(target);
|
||||
}
|
||||
for (const name of readdirSync(existing).filter(nameAllowed)) {
|
||||
const file = join(existing, name);
|
||||
if (!statSync(file, { throwIfNoEntry: false })?.isFile()) continue;
|
||||
cpSync(file, join(destination, name), { dereference: true, preserveTimestamps: true });
|
||||
}
|
||||
};
|
||||
// External mounts remain untouched. Absolute paths inside next/ rebase into the new release.
|
||||
if (insideCurrent) {
|
||||
const destination = resolve(staging, suffix);
|
||||
const candidates = [source, join(current, 'template/pages/sitemaps'), join(current, 'data/sitemaps'), join(original, 'template/pages/sitemaps')];
|
||||
const existing = candidates.find(available);
|
||||
if (existing && existing !== destination) preserveDirectory(existing, destination);
|
||||
}
|
||||
// The original router's second index candidate is independent of SITEMAP_DIR.
|
||||
const fallback = [join(current, 'template/pages/sitemap.xml'), join(original, 'template/pages/sitemap.xml')].find(file => existsSync(file) && statSync(file).isFile());
|
||||
if (fallback) {
|
||||
const target = join(staging, 'template/pages/sitemap.xml');
|
||||
ensureParent(target);
|
||||
if (info(target)?.isSymbolicLink()) rmSync(target);
|
||||
cpSync(fallback, target, { dereference: true, preserveTimestamps: true });
|
||||
}
|
||||
// Preserve the same optional robots overrides that the original Go helper reads.
|
||||
for (const file of ['template/pages/robots.txt', 'templates/pages/robots.txt', 'app/template/pages/robots.txt', 'app/templates/pages/robots.txt']) {
|
||||
const existing = [join(current, file), join(original, file)].find(source => existsSync(source) && statSync(source).isFile());
|
||||
if (!existing) continue;
|
||||
const target = join(staging, file);
|
||||
ensureParent(target);
|
||||
if (info(target)?.isSymbolicLink()) rmSync(target);
|
||||
cpSync(existing, target, { dereference: true, preserveTimestamps: true });
|
||||
}
|
||||
NODE
|
||||
)
|
||||
# END preserve backend sitemaps
|
||||
APP_PORT=$(cd "$STAGING" && "$NODE_BIN" --input-type=module <<'NODE'
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { parse } from 'dotenv';
|
||||
const read = path => existsSync(path) ? parse(readFileSync(path)) : {};
|
||||
const config = { ...read('config/test.env'), ...read('config/test.local.env'), ...process.env };
|
||||
const port = config.PORT || '30334';
|
||||
if (!/^\d+$/.test(port) || Number(port) < 1 || Number(port) > 65535) throw new Error('Invalid PORT');
|
||||
if (!/^https?:\/\//.test(config.UPSTREAM_API_URL?.trim() || '')) throw new Error('UPSTREAM_API_URL is required');
|
||||
console.log(Number(port));
|
||||
NODE
|
||||
)
|
||||
|
||||
mkdir -p "$SUP_CONF_DIR" "$SUP_LOG_DIR"
|
||||
NEW_CONF=$(mktemp "$DIR/.${PROGRAM}.supervisor.XXXXXX")
|
||||
# Supervisor 会解析百分号;路径中的引号与反斜杠同时按其 command 语法转义。
|
||||
supervisor_quote() { local value=${1//\\/\\\\}; value=${value//\"/\\\"}; printf '%s' "${value//%/%%}"; }
|
||||
cat > "$NEW_CONF" <<CONF
|
||||
[program:$PROGRAM]
|
||||
directory=$(supervisor_quote "$CURRENT")
|
||||
command="$(supervisor_quote "$NODE_BIN")" "$(supervisor_quote "$CURRENT/start.mjs")"
|
||||
environment=HOSTNAME="0.0.0.0",PORT="$APP_PORT",APP_ENV="test",NODE_ENV="production",PATH="$(supervisor_quote "$PATH")"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=3
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
stopwaitsecs=15
|
||||
stdout_logfile=$(supervisor_quote "$SUP_LOG_DIR/$PROGRAM.log")
|
||||
stderr_logfile=$(supervisor_quote "$SUP_LOG_DIR/${PROGRAM}_error.log")
|
||||
CONF
|
||||
if [ -f "$CONF" ]; then
|
||||
HAD_CONF=1
|
||||
CONF_BACKUP="$CONF.bak.$STAMP"
|
||||
cp -p "$CONF" "$CONF_BACKUP"
|
||||
fi
|
||||
|
||||
# 依赖与配置检查全部通过后才停止当前 Node;切换后的任何失败都会自动恢复。
|
||||
SWITCHED=1
|
||||
if [ "$WAS_RUNNING" = 1 ]; then supervisorctl stop "$PROGRAM"; fi
|
||||
if [ -d "$CURRENT" ]; then
|
||||
rm -rf "$PREVIOUS"
|
||||
mv "$CURRENT" "$PREVIOUS"
|
||||
OLD_MOVED=1
|
||||
fi
|
||||
mv "$STAGING" "$CURRENT"
|
||||
INSTALLED=1
|
||||
STAGING=
|
||||
CONF_CHANGED=1
|
||||
cp "$NEW_CONF" "$CONF"
|
||||
chmod 644 "$CONF"
|
||||
supervisorctl reread
|
||||
supervisorctl update
|
||||
supervisorctl restart "$PROGRAM"
|
||||
|
||||
BASE="http://127.0.0.1:$APP_PORT"
|
||||
HEALTH_FILE=$(mktemp "$DIR/.${PROGRAM}.health.XXXXXX")
|
||||
HEALTH_OK=0
|
||||
for attempt in $(seq 1 20); do
|
||||
CODE=$(curl -sS --connect-timeout 2 --max-time 5 -o "$HEALTH_FILE" -w '%{http_code}' "$BASE/healthz" 2>/dev/null || true)
|
||||
if [ "$CODE" = 200 ] && "$NODE_BIN" -e '
|
||||
try { const value=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
|
||||
process.exit(value.status==="ok" && value.environment==="test" ? 0 : 1);
|
||||
} catch { process.exit(1); }' "$HEALTH_FILE"; then HEALTH_OK=1; break; fi
|
||||
sleep 1
|
||||
done
|
||||
[ "$HEALTH_OK" = 1 ] || fail '/healthz 未返回测试环境的 HTTP 200 健康响应'
|
||||
CODE=$(curl -sS --connect-timeout 2 --max-time 45 -o /dev/null -w '%{http_code}' "$BASE/" || true)
|
||||
[ "$CODE" = 200 ] || fail "首页检查失败,HTTP $CODE"
|
||||
CODE=$(curl -sS --connect-timeout 2 --max-time 25 -o "$HEALTH_FILE" -w '%{http_code}' "$BASE/enkey" || true)
|
||||
[ "$CODE" = 200 ] && [ -s "$HEALTH_FILE" ] || fail "/enkey 检查失败,HTTP $CODE 或密钥为空"
|
||||
supervisorctl status "$PROGRAM"
|
||||
SWITCHED=0
|
||||
# 新上传若已覆盖固定文件名,留给下一次发布;清理失败也不改变已通过检查的结果。
|
||||
LATEST=$(sha256sum "$PACKAGE" 2>/dev/null | awk '{print $1}' || true)
|
||||
if [ "$LATEST" = "$ACTUAL" ]; then
|
||||
rm -f "$PACKAGE" "$CHECKSUM" || echo '提示:服务已更新,但上传包清理失败。' >&2
|
||||
else echo '提示:上传包已变更或不存在,保留当前文件供下次发布。'; fi
|
||||
echo "测试更新完成:${CURRENT};/healthz、首页、/enkey 检查通过。"
|
||||
echo "请确认 NGINX 将页面及 /enkey、/newenkey、/h5/m3u8/ 代理到 127.0.0.1:${APP_PORT};脚本未修改 NGINX。"
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/bin/bash
|
||||
# 花魁短剧正式服:检查 → 构建 → Linux 运行包 + MD5 → 跳板机 running.sh。
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
cd "$SCRIPT_DIR"
|
||||
JUMP_HOST="${DEPLOY_JUMP_HOST:-server@116.212.120.2}"
|
||||
JUMP_DIR="${DEPLOY_JUMP_DIR:-/home/server/web_hkdj/h5/nuxt}"
|
||||
# 按正式服约定分别设置:SCP 为 22889,触发 SSH 为默认 22。
|
||||
SCP_PORT="${DEPLOY_SCP_PORT:-22889}"
|
||||
SSH_PORT="${DEPLOY_SSH_PORT:-22}"
|
||||
TARGET_ARCH="${DEPLOY_TARGET_ARCH:-x64}"
|
||||
TARGET_LIBC="${DEPLOY_TARGET_LIBC:-glibc}"
|
||||
PACKAGE_NAME=web_hkdj_h5.tar.gz
|
||||
MD5_NAME=web_hkdj_h5.md5
|
||||
PACKAGE="$SCRIPT_DIR/$PACKAGE_NAME"
|
||||
MD5_FILE="$SCRIPT_DIR/$MD5_NAME"
|
||||
RELEASE="$SCRIPT_DIR/dist/production-linux-$TARGET_ARCH-$TARGET_LIBC"
|
||||
PACKAGE_ONLY=0
|
||||
|
||||
fail() { printf '错误:%s\n' "$*" >&2; exit 1; }
|
||||
step() { printf '\n▸ %s\n' "$*"; }
|
||||
for argument in "$@"; do
|
||||
case "$argument" in
|
||||
--dry-run) DEPLOY_DRY_RUN=1 ;;
|
||||
--package-only) PACKAGE_ONLY=1 ;;
|
||||
-h|--help)
|
||||
printf '用法:%s [--dry-run | --package-only]\n' "$0"
|
||||
printf '默认上传 server@116.212.120.2:/home/server/web_hkdj/h5/nuxt/,SCP 22889,SSH 22。\n'
|
||||
printf '可覆盖 DEPLOY_JUMP_HOST、DEPLOY_JUMP_DIR、DEPLOY_SCP_PORT、DEPLOY_SSH_PORT、DEPLOY_TARGET_ARCH、DEPLOY_TARGET_LIBC。\n'
|
||||
exit 0 ;;
|
||||
*) fail "不支持的参数 $argument;使用 --help 查看用法" ;;
|
||||
esac
|
||||
done
|
||||
[[ "$JUMP_HOST" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.@-]*$ ]] || fail 'DEPLOY_JUMP_HOST 格式无效'
|
||||
[[ "$JUMP_DIR" =~ ^/[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*/?$ ]] || fail 'DEPLOY_JUMP_DIR 必须是普通绝对路径'
|
||||
JUMP_DIR="${JUMP_DIR%/}"
|
||||
for port in "$SCP_PORT" "$SSH_PORT"; do
|
||||
[[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ] || fail 'SCP/SSH 端口必须在 1–65535 内'
|
||||
done
|
||||
case "$TARGET_ARCH" in x64|arm64) ;; *) fail 'DEPLOY_TARGET_ARCH 仅支持 x64 或 arm64' ;; esac
|
||||
case "$TARGET_LIBC" in glibc|musl) ;; *) fail 'DEPLOY_TARGET_LIBC 仅支持 glibc 或 musl' ;; esac
|
||||
for tool in node npm tar; do
|
||||
command -v "$tool" >/dev/null || fail "缺少命令 $tool"
|
||||
done
|
||||
node -e 'const [a,b]=process.versions.node.split(".").map(Number); if(a<20 || a===20 && b<9) process.exit(1)' || fail '需要 Node.js >=20.9,推荐 22'
|
||||
node -e 'const [a,b,c]=process.argv[1].split(".").map(Number); if(a<10 || a===10 && (b<9 || b===9 && c<2)) process.exit(1)' "$(npm --version)" || fail '跨平台运行依赖打包需要 npm >=10.9.2'
|
||||
command -v md5sum >/dev/null || command -v md5 >/dev/null || fail '缺少 md5sum 或 macOS md5'
|
||||
for file in config/production.env config/secrets/m3u8.key package-lock.json scripts/prepare-production-runtime.ts; do
|
||||
[ -s "$file" ] || fail "缺少 $file"
|
||||
done
|
||||
[ -x node_modules/.bin/tsx ] || fail '请先执行 npm ci 安装本地依赖'
|
||||
|
||||
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
|
||||
printf 'DRY RUN:正式环境;Linux %s/%s;只核对,不构建、不连接服务器。\n' "$TARGET_ARCH" "$TARGET_LIBC"
|
||||
node --input-type=module -e 'import {readFileSync} from "node:fs"; import {parse} from "dotenv"; const c=parse(readFileSync("config/production.env")); console.log(` 服务端口 PORT=${c.PORT};仅打包 config/production.env,不上传本地覆盖。`)'
|
||||
printf ' npm run check → build → bundle → bundle:linux -- %s %s\n' "$TARGET_ARCH" "$TARGET_LIBC"
|
||||
printf ' 保留 standalone 裁剪依赖,仅补目标 Linux Sharp/libvips;不安装完整生产依赖。\n'
|
||||
printf ' tar 根目录:start.mjs、server.js、node_modules、.next、public、config 等\n'
|
||||
printf ' 生成 %s + %s(纯 MD5 hash)\n' "$PACKAGE_NAME" "$MD5_NAME"
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then
|
||||
printf ' --package-only:生成后结束,不上传。\n'
|
||||
else
|
||||
printf ' scp -P %s %s %s %s:%s/\n' "$SCP_PORT" "$PACKAGE_NAME" "$MD5_NAME" "$JUMP_HOST" "$JUMP_DIR"
|
||||
printf ' ssh -p %s %s %s/running.sh(不带脚本参数)\n' "$SSH_PORT" "$JUMP_HOST" "$JUMP_DIR"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
if [ "$PACKAGE_ONLY" = 0 ]; then
|
||||
for tool in ssh scp tee; do command -v "$tool" >/dev/null || fail "缺少命令 $tool"; done
|
||||
fi
|
||||
|
||||
# 同仓库只能有一次构建/打包/上传,避免并发覆盖同名包或提前触发 running.sh。
|
||||
mkdir -p "$SCRIPT_DIR/dist"
|
||||
DEPLOY_LOCK="$SCRIPT_DIR/dist/.production-deploy.lock"
|
||||
mkdir "$DEPLOY_LOCK" 2>/dev/null || fail "已有正式发布进程或残留锁:${DEPLOY_LOCK};请先确认该进程已结束"
|
||||
trap 'rmdir "$DEPLOY_LOCK" 2>/dev/null || true' EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
step '检查类型和单元测试'
|
||||
npm run check
|
||||
step '构建并整理正式环境 standalone'
|
||||
npm run build
|
||||
npm run bundle
|
||||
step "准备 standalone 精简运行包,仅补 Linux $TARGET_ARCH/$TARGET_LIBC 图片库"
|
||||
npm run bundle:linux -- "$TARGET_ARCH" "$TARGET_LIBC"
|
||||
for file in start.mjs server.js .next/BUILD_ID config/production.env config/secrets/m3u8.key runtime-target.json; do
|
||||
[ -s "$RELEASE/$file" ] || fail "正式运行包缺少 $file"
|
||||
done
|
||||
for directory in node_modules .next/static public; do
|
||||
[ -d "$RELEASE/$directory" ] || fail "正式运行包缺少目录 $directory"
|
||||
done
|
||||
|
||||
step "打包 $PACKAGE_NAME"
|
||||
# -C 保持产物直接位于 tar 根部;禁止 AppleDouble、环境覆盖和日志混入。
|
||||
COPYFILE_DISABLE=1 COPY_EXTENDED_ATTRIBUTES_DISABLE=1 tar \
|
||||
--no-xattrs \
|
||||
--exclude='._*' --exclude='.DS_Store' --exclude='.env*' \
|
||||
--exclude='*.local.env' --exclude='*.log' \
|
||||
-czf "$PACKAGE" -C "$RELEASE" .
|
||||
chmod 600 "$PACKAGE"
|
||||
if command -v md5sum >/dev/null; then
|
||||
md5sum "$PACKAGE" | awk '{print $1}' > "$MD5_FILE"
|
||||
else
|
||||
md5 -q "$PACKAGE" > "$MD5_FILE"
|
||||
fi
|
||||
tar -tzf "$PACKAGE" >/dev/null
|
||||
printf '运行包已生成:%s;校验文件:%s\n' "$PACKAGE_NAME" "$MD5_NAME"
|
||||
node -e 'console.log(`压缩包体积:${(require("node:fs").statSync(process.argv[1]).size / 1024 / 1024).toFixed(2)} MiB`)' "$PACKAGE"
|
||||
if [ "$PACKAGE_ONLY" = 1 ]; then exit 0; fi
|
||||
|
||||
step "上传到跳板机 $JUMP_HOST:$JUMP_DIR/"
|
||||
scp -P "$SCP_PORT" "$PACKAGE" "$MD5_FILE" "$JUMP_HOST:$JUMP_DIR/"
|
||||
step '执行既有正式服 running.sh'
|
||||
# gatekeeper 按完整 .sh 路径放行,不能附参数、cd、bash 前缀或其它命令。
|
||||
RUN_LOG=$(mktemp "$SCRIPT_DIR/dist/production-running.XXXXXX")
|
||||
printf '远端执行日志:%s\n' "$RUN_LOG"
|
||||
if ! ssh -p "$SSH_PORT" "$JUMP_HOST" "$JUMP_DIR/running.sh" 2>&1 | tee "$RUN_LOG"; then
|
||||
fail "正式服 running.sh 执行失败;日志:${RUN_LOG}"
|
||||
fi
|
||||
# 部分既有服务器脚本没有透传 Supervisor 的失败退出码,不能只依据 SSH 返回 0。
|
||||
if ! node - "$RUN_LOG" <<'NODE'
|
||||
const { readFileSync } = require('node:fs');
|
||||
const output = readFileSync(process.argv[2], 'utf8').replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '');
|
||||
if (/\b(?:BACKOFF|FATAL|ERROR)\b|\bspawn\s+error\b/i.test(output)) process.exit(1);
|
||||
NODE
|
||||
then
|
||||
fail "正式服输出包含失败状态,不能确认发布成功;日志:${RUN_LOG}"
|
||||
fi
|
||||
printf '\n正式服 running.sh 执行完成,未发现失败状态;日志:%s;本地包已保留供排查。\n' "$RUN_LOG"
|
||||
@@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
# 花魁短剧测试服:检查 → 构建 → dist.zip → 上传 → Supervisor 重启/健康检查。
|
||||
# DEPLOY_DRY_RUN=1 ./deploy_test.sh 仅核对,不构建、不连接服务器。
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
HOST="${DEPLOY_HOST:-root@172.104.181.99}"
|
||||
SSH_PORT="${DEPLOY_SSH_PORT:-22889}"
|
||||
REMOTE_DIR="${DEPLOY_DIR:-/server/hkdj_net/h5}"
|
||||
CHECK_URL="${DEPLOY_CHECK_URL:-https://hkdjneth5.remoces.com}"
|
||||
PACKAGE="$SCRIPT_DIR/dist.zip"
|
||||
CHECKSUM="$SCRIPT_DIR/dist.zip.sha256"
|
||||
|
||||
fail() { printf '错误:%s\n' "$*" >&2; exit 1; }
|
||||
step() { printf '\n▸ %s\n' "$*"; }
|
||||
|
||||
for argument in "$@"; do
|
||||
case "$argument" in
|
||||
--dry-run) DEPLOY_DRY_RUN=1 ;;
|
||||
-h|--help)
|
||||
printf '用法:%s [--dry-run]\n默认目标 root@172.104.181.99:22889 /server/hkdj_net/h5/\n可覆盖 DEPLOY_HOST、DEPLOY_SSH_PORT、DEPLOY_DIR、DEPLOY_CHECK_URL;DEPLOY_DRY_RUN=1 等同 --dry-run。\n' "$0"
|
||||
exit 0 ;;
|
||||
*) fail "不支持的参数 $argument;使用 --help 查看用法" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# 这些值会作为远端路径/命令使用,仅允许普通 SSH 目标和绝对路径。
|
||||
[[ "$HOST" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_.@-]*$ ]] || fail "DEPLOY_HOST 格式无效"
|
||||
[[ "$SSH_PORT" =~ ^[0-9]+$ ]] && [ "$SSH_PORT" -ge 1 ] && [ "$SSH_PORT" -le 65535 ] || fail "DEPLOY_SSH_PORT 无效"
|
||||
[[ "$REMOTE_DIR" =~ ^/[a-zA-Z0-9_-]+(/[a-zA-Z0-9_-]+)*/?$ ]] || fail "DEPLOY_DIR 必须是普通绝对路径"
|
||||
|
||||
for tool in node npm zip unzip ssh scp; do
|
||||
command -v "$tool" >/dev/null || fail "缺少命令 $tool"
|
||||
done
|
||||
node -e 'const [a,b]=process.versions.node.split(".").map(Number); if(a<20 || (a===20 && b<9)) process.exit(1)' || fail "需要 Node.js >=20.9,推荐 Node.js 22"
|
||||
node -e 'try { const u=new URL(process.argv[1]); if (!["http:","https:"].includes(u.protocol) || u.username || u.password || u.search || u.hash || u.pathname!=="/") process.exit(1); } catch { process.exit(1); }' "$CHECK_URL" || fail "DEPLOY_CHECK_URL 必须是 HTTP(S) 站点根地址"
|
||||
for file in config/test.env config/secrets/m3u8.key deploy/up_next_h5.sh package-lock.json; do
|
||||
[ -s "$file" ] || fail "缺少 $file"
|
||||
done
|
||||
bash -n deploy/up_next_h5.sh
|
||||
|
||||
if [ "${DEPLOY_DRY_RUN:-0}" = 1 ]; then
|
||||
printf 'DRY RUN:测试配置 config/test.env;上游与端口可由服务器 config/test.local.env 覆盖。\n'
|
||||
# 不输出本地覆盖/进程配置中的凭证;只展示仓库公共配置。
|
||||
node --input-type=module -e 'import {readFileSync} from "node:fs"; import {parse} from "dotenv"; const c=parse(readFileSync("config/test.env")); console.log(` API=${c.UPSTREAM_API_URL} PORT=${c.PORT}`)'
|
||||
printf ' npm run check → npm run build:test → npm run bundle:test\n'
|
||||
printf ' dist.zip(不含 node_modules / 本地覆盖)+ SHA-256 → %s:%s/(SSH %s)\n' "$HOST" "$REMOTE_DIR" "$SSH_PORT"
|
||||
printf ' ssh 执行 %s/up_next_h5.sh:安装 Linux 运行依赖 → next/ → Supervisor hkdj_next_h5 → 健康检查\n' "$REMOTE_DIR"
|
||||
printf ' 公网检查 %s:/healthz(test)+ 首页(Next HTML);此模式不访问域名。\n' "$CHECK_URL"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
step '检查类型和单元测试'
|
||||
npm run check
|
||||
step '构建并整理测试环境 standalone'
|
||||
npm run build:test
|
||||
npm run bundle:test
|
||||
|
||||
for file in start.mjs server.js .next-test/BUILD_ID config/test.env config/secrets/m3u8.key; do
|
||||
[ -s "dist/test/$file" ] || fail "测试包缺少 $file"
|
||||
done
|
||||
[ -d dist/test/.next-test/static ] && [ -d dist/test/public ] || fail '测试包缺少静态资源'
|
||||
|
||||
# Next 本机的原生模块不能直接用于 Linux:远端用 lockfile 安装运行依赖。
|
||||
# dotenv 属于 dependencies,start.mjs 在 npm ci --omit=dev 后仍可加载配置。
|
||||
cp package.json package-lock.json dist/test/
|
||||
step '生成 dist.zip 和 SHA-256(保留本地包便于排查)'
|
||||
rm -f "$PACKAGE" "$CHECKSUM"
|
||||
(
|
||||
cd dist/test
|
||||
COPYFILE_DISABLE=1 zip -q -r "$PACKAGE" . \
|
||||
-x 'node_modules/*' '*/node_modules/*' '*.local.env' '.env*' '*/.env*' '._*' '*/._*' '*.log'
|
||||
)
|
||||
chmod 600 "$PACKAGE"
|
||||
if command -v sha256sum >/dev/null; then
|
||||
sha256sum dist.zip > "$CHECKSUM"
|
||||
else
|
||||
shasum -a 256 dist.zip > "$CHECKSUM"
|
||||
fi
|
||||
unzip -tq "$PACKAGE"
|
||||
|
||||
step "上传到 $HOST:$REMOTE_DIR/"
|
||||
ssh -p "$SSH_PORT" "$HOST" "mkdir -p '$REMOTE_DIR'"
|
||||
scp -P "$SSH_PORT" "$PACKAGE" "$CHECKSUM" deploy/up_next_h5.sh "$HOST:$REMOTE_DIR/"
|
||||
step '执行服务器更新脚本'
|
||||
ssh -p "$SSH_PORT" "$HOST" "/bin/bash '$REMOTE_DIR/up_next_h5.sh'"
|
||||
step "检查公网测试站 $CHECK_URL"
|
||||
if ! node --input-type=module - "$CHECK_URL" <<'NODE'
|
||||
const origin = process.argv[2];
|
||||
async function read(path) {
|
||||
const response = await fetch(new URL(path, origin), { signal: AbortSignal.timeout(4000), redirect: 'error', cache: 'no-store' });
|
||||
if (response.status !== 200) throw new Error(`${path} HTTP ${response.status}`);
|
||||
let text = '', size = 0;
|
||||
const decoder = new TextDecoder();
|
||||
for await (const part of response.body) {
|
||||
size += part.length;
|
||||
if (size > 262144) throw new Error(`${path} 响应超过检查上限`);
|
||||
text += decoder.decode(part, { stream: true });
|
||||
if (path === '/' && text.includes('/_next/')) break;
|
||||
}
|
||||
return { text, type: response.headers.get('content-type') || '' };
|
||||
}
|
||||
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||
try {
|
||||
const [health, home] = await Promise.all([read('/healthz'), read('/')]);
|
||||
let status;
|
||||
try { status = JSON.parse(health.text); } catch { throw new Error('/healthz 返回无效 JSON'); }
|
||||
if (status?.status !== 'ok' || status?.environment !== 'test') throw new Error('/healthz 未命中 Next 测试服务');
|
||||
if (!/^text\/html(?:;|$)/i.test(home.type) || !home.text.includes('/_next/')) throw new Error('首页不是 Next HTML');
|
||||
console.log('公网 /healthz 与 Next 首页检查通过');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error(`公网检查 ${attempt}/3 失败:${error.message}`);
|
||||
if (attempt < 3) await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
process.exit(1);
|
||||
NODE
|
||||
then
|
||||
fail '服务器服务已更新,但域名检查失败,请检查 Nginx 和 Cloudflare 回源配置。'
|
||||
fi
|
||||
printf '\n测试服更新完成:%s:%s/next(Supervisor:hkdj_next_h5)\n' "$HOST" "$REMOTE_DIR"
|
||||
@@ -0,0 +1,7 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next-dev/dev/types/routes.d.ts";
|
||||
import "./.next-dev/dev/types/root-params.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const env = process.env.APP_ENV || "development";
|
||||
const config: NextConfig = {
|
||||
output: "standalone",
|
||||
distDir:
|
||||
env === "production"
|
||||
? ".next"
|
||||
: env === "test"
|
||||
? ".next-test"
|
||||
: ".next-dev",
|
||||
poweredByHeader: false,
|
||||
// Go serves generated files without application compression; keep file byte lengths and ranges intact.
|
||||
// Public reverse proxies may apply the same transport compression used for the original Go service.
|
||||
compress: false,
|
||||
htmlLimitedBots: /.*/, // Keep complete SEO metadata in the initial HTML for every crawler/client.
|
||||
// Preserve source canonical redirects in proxy.ts before React rendering.
|
||||
skipTrailingSlashRedirect: true,
|
||||
skipProxyUrlNormalize: true,
|
||||
outputFileTracingIncludes: {
|
||||
"/*": [
|
||||
"./config/secrets/m3u8.key",
|
||||
"./template/pages/robots.txt",
|
||||
"./templates/pages/robots.txt",
|
||||
"./app/template/pages/robots.txt",
|
||||
"./app/templates/pages/robots.txt",
|
||||
"./config/seo/**/*",
|
||||
],
|
||||
},
|
||||
// Keep XML out of page traces. package-standalone.ts also removes copies from Node Proxy traces.
|
||||
outputFileTracingExcludes: {
|
||||
"/*": [
|
||||
"./template/pages/sitemaps/**/*",
|
||||
"./template/pages/sitemap.xml",
|
||||
"./data/sitemaps/**/*",
|
||||
],
|
||||
},
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: "/sw.js",
|
||||
headers: [
|
||||
{ key: "Content-Type", value: "application/javascript; charset=utf-8" },
|
||||
{ key: "Cache-Control", value: "no-cache, no-store, must-revalidate" },
|
||||
{ key: "Service-Worker-Allowed", value: "/" },
|
||||
],
|
||||
},
|
||||
{
|
||||
source: "/static/:path*",
|
||||
headers: [
|
||||
{
|
||||
key: "Cache-Control",
|
||||
value: "public, max-age=604800, stale-while-revalidate=86400",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "huakui-drama-web",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "tsx scripts/next-env.ts development dev",
|
||||
"dev:charles": "bash scripts/dev-charles.sh",
|
||||
"dev:test": "tsx scripts/next-env.ts test dev",
|
||||
"build": "tsx scripts/next-env.ts production build",
|
||||
"build:dev": "tsx scripts/next-env.ts development build",
|
||||
"build:test": "tsx scripts/next-env.ts test build",
|
||||
"start": "tsx scripts/next-env.ts production start",
|
||||
"start:dev": "tsx scripts/next-env.ts development start",
|
||||
"start:test": "tsx scripts/next-env.ts test start",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "tsx --test tests/*.test.ts tests/*.test.tsx",
|
||||
"check": "npm run typecheck && npm test",
|
||||
"test:browser": "tsx scripts/browser-smoke.ts",
|
||||
"test:ui": "tsx scripts/ui-check.ts",
|
||||
"test:actions": "tsx scripts/catalog-react-smoke.ts",
|
||||
"test:popups": "tsx scripts/native-site-smoke.ts",
|
||||
"test:install": "tsx scripts/native-site-smoke.ts",
|
||||
"assets:sync": "tsx scripts/sync-figma-assets.ts && tsx scripts/sync-figma-icons.ts && tsx scripts/compose-placeholder.ts",
|
||||
"assets:check": "tsx scripts/sync-figma-assets.ts --check && tsx scripts/sync-figma-icons.ts --check && tsx scripts/compose-placeholder.ts --check",
|
||||
"assets:pwa": "tsx scripts/compose-pwa-icons.ts && tsx scripts/build-pwa.ts",
|
||||
"assets:pwa:check": "tsx scripts/compose-pwa-icons.ts --check && tsx scripts/build-pwa.ts --check",
|
||||
"bundle": "tsx scripts/package-standalone.ts production",
|
||||
"bundle:test": "tsx scripts/package-standalone.ts test",
|
||||
"bundle:linux": "tsx scripts/prepare-production-runtime.ts",
|
||||
"deploy:test": "bash deploy_test.sh",
|
||||
"deploy:prod": "bash deploy_prod.sh",
|
||||
"test:catalog": "tsx scripts/catalog-react-smoke.ts",
|
||||
"test:playback": "tsx scripts/playback-smoke.ts",
|
||||
"test:pwa": "tsx scripts/pwa-smoke.ts",
|
||||
"test:presentation": "tsx scripts/page-presentation-smoke.ts",
|
||||
"test:ssr": "tsx scripts/native-ssr-smoke.ts",
|
||||
"sitemap:sync": "tsx scripts/sync-sitemap.ts",
|
||||
"routes:export": "tsx scripts/export-page-routes.ts",
|
||||
"routes:check": "tsx scripts/export-page-routes.ts --check"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^17.2.3",
|
||||
"next": "16.4.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "19.3.0",
|
||||
"react-dom": "19.3.0",
|
||||
"xgplayer": "3.0.26",
|
||||
"xgplayer-hls": "3.0.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.56.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"esbuild": "0.28.2",
|
||||
"sharp": "0.35.5",
|
||||
"tsx": "^4.20.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"fileKey": "1vUq4wf5xOs8ZOlmt26Q3O",
|
||||
"source": "https://www.figma.com/design/1vUq4wf5xOs8ZOlmt26Q3O/?node-id=0-1",
|
||||
"assets": [
|
||||
{
|
||||
"id": "1:414",
|
||||
"name": "header",
|
||||
"file": "public/static/brand/header.svg",
|
||||
"width": 104,
|
||||
"height": 36,
|
||||
"sha256": "1e58739a5d3253b2dd6f5e5903609bf228445397cb9532f695e7d4b60a67c84e"
|
||||
},
|
||||
{
|
||||
"id": "1:706",
|
||||
"name": "footer",
|
||||
"file": "public/static/brand/footer.svg",
|
||||
"width": 124,
|
||||
"height": 44,
|
||||
"sha256": "5268c270113a1e55e6d56e8f934f317ef2e7e357ae472373160a3bc96b61bd6e"
|
||||
},
|
||||
{
|
||||
"id": "3:20211",
|
||||
"name": "mobile",
|
||||
"file": "public/static/brand/mobile.svg",
|
||||
"width": 88,
|
||||
"height": 28,
|
||||
"sha256": "65f185eafddb14c72738b97782b018e6379284989b828500af7f8bfabd161258"
|
||||
},
|
||||
{
|
||||
"id": "3:15722",
|
||||
"name": "symbol",
|
||||
"file": "public/static/favicon.svg",
|
||||
"width": 34,
|
||||
"height": 34,
|
||||
"sha256": "f1f80d0ea052d49d405e5ce3ea80d2f6a511c2b68ac4094e330434f2b883f603"
|
||||
},
|
||||
{
|
||||
"id": "1:414",
|
||||
"name": "header-png",
|
||||
"file": "public/static/logo.png",
|
||||
"width": 208,
|
||||
"height": 72,
|
||||
"sha256": "5791140918967fd5b92718b89f7648a28f8da59a2df76b517cdeb0203f850c5d"
|
||||
},
|
||||
{
|
||||
"id": "1:706",
|
||||
"name": "footer-png",
|
||||
"file": "public/static/logo2.png",
|
||||
"width": 248,
|
||||
"height": 88,
|
||||
"sha256": "db8d678831257b6776cbee9e4cd752c7a360cba098634787879f819920b64c49"
|
||||
},
|
||||
{
|
||||
"id": "3:21847",
|
||||
"name": "login-symbol",
|
||||
"file": "public/static/logo_icon.png",
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"sha256": "bd08f5199d6d4bc7831f3b7863f6d2ba6496750ff65aff39ea23305131e53403"
|
||||
},
|
||||
{
|
||||
"id": "3:15722",
|
||||
"name": "favicon-png",
|
||||
"file": "public/static/favicon.png",
|
||||
"width": 68,
|
||||
"height": 68,
|
||||
"sha256": "433a136adc0416be48822a3ae9d214beb0096e7a69be1b7803e5e8788f0dc70f"
|
||||
},
|
||||
{
|
||||
"id": "3:15722",
|
||||
"name": "favicon-ico",
|
||||
"file": "public/static/favicon.ico",
|
||||
"width": 68,
|
||||
"height": 68,
|
||||
"sha256": "f9c5f3c9b35123fae480286215c10334b2ea75d35e84770e9aa10a12c8756ff9"
|
||||
},
|
||||
{
|
||||
"id": "1:414",
|
||||
"name": "header-png-versioned",
|
||||
"file": "public/static/brand/huakui-20261013/header.png",
|
||||
"width": 208,
|
||||
"height": 72,
|
||||
"sha256": "5791140918967fd5b92718b89f7648a28f8da59a2df76b517cdeb0203f850c5d",
|
||||
"source": "public/static/logo.png"
|
||||
},
|
||||
{
|
||||
"id": "1:706",
|
||||
"name": "footer-png-versioned",
|
||||
"file": "public/static/brand/huakui-20261013/footer.png",
|
||||
"width": 248,
|
||||
"height": 88,
|
||||
"sha256": "db8d678831257b6776cbee9e4cd752c7a360cba098634787879f819920b64c49",
|
||||
"source": "public/static/logo2.png"
|
||||
},
|
||||
{
|
||||
"id": "3:21847",
|
||||
"name": "login-symbol-versioned",
|
||||
"file": "public/static/brand/huakui-20261013/auth.png",
|
||||
"width": 128,
|
||||
"height": 128,
|
||||
"sha256": "bd08f5199d6d4bc7831f3b7863f6d2ba6496750ff65aff39ea23305131e53403",
|
||||
"source": "public/static/logo_icon.png"
|
||||
},
|
||||
{
|
||||
"id": "3:15722",
|
||||
"name": "symbol-versioned",
|
||||
"file": "public/static/brand/huakui-20261013/icon.svg",
|
||||
"width": 34,
|
||||
"height": 34,
|
||||
"sha256": "f1f80d0ea052d49d405e5ce3ea80d2f6a511c2b68ac4094e330434f2b883f603",
|
||||
"source": "public/static/favicon.svg"
|
||||
},
|
||||
{
|
||||
"id": "3:15722",
|
||||
"name": "favicon-png-versioned",
|
||||
"file": "public/static/brand/huakui-20261013/icon.png",
|
||||
"width": 68,
|
||||
"height": 68,
|
||||
"sha256": "433a136adc0416be48822a3ae9d214beb0096e7a69be1b7803e5e8788f0dc70f",
|
||||
"source": "public/static/favicon.png"
|
||||
},
|
||||
{
|
||||
"id": "3:15722",
|
||||
"name": "favicon-ico-versioned",
|
||||
"file": "public/static/brand/huakui-20261013/favicon.ico",
|
||||
"width": 68,
|
||||
"height": 68,
|
||||
"sha256": "f9c5f3c9b35123fae480286215c10334b2ea75d35e84770e9aa10a12c8756ff9",
|
||||
"source": "public/static/favicon.ico"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"file": "public/static/brand/placeholder.svg",
|
||||
"width": 480,
|
||||
"height": 480,
|
||||
"sha256": "08a739c4d5f374c44a3bee35d32256334b8b79fff2f31c6c78bab10e3afb6284",
|
||||
"source": "Composition using the unmodified Figma brand export and the design panel background token.",
|
||||
"background": {
|
||||
"color": "#26272f",
|
||||
"source": "Figma global panel token"
|
||||
},
|
||||
"composition": {
|
||||
"type": "svg-container",
|
||||
"embeddedImage": {
|
||||
"file": "public/static/brand/footer.svg",
|
||||
"source": "https://www.figma.com/design/1vUq4wf5xOs8ZOlmt26Q3O/?node-id=1-706",
|
||||
"nodeId": "1:706",
|
||||
"sha256": "5268c270113a1e55e6d56e8f934f317ef2e7e357ae472373160a3bc96b61bd6e",
|
||||
"encoding": "base64",
|
||||
"unmodified": true,
|
||||
"x": 120,
|
||||
"y": 197.419355,
|
||||
"width": 240,
|
||||
"height": 85.16129,
|
||||
"opacity": 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="64" height="64" rx="32" fill="#FF0F53" fill-opacity="0.1"/>
|
||||
<rect x="0.5" y="0.5" width="63" height="63" rx="31.5" stroke="#FF0F53" stroke-opacity="0.3"/>
|
||||
<path d="M20.14 24.72H21.9V39H19.56V27.54C18.7 28.32 17.62 28.9 16.3 29.28V26.96C16.94 26.8 17.62 26.52 18.34 26.12C19.06 25.68 19.66 25.22 20.14 24.72ZM29.7031 24.44C31.2231 24.44 32.4431 24.84 33.3631 25.66C34.1631 26.38 34.5831 27.28 34.5831 28.36C34.5831 29.08 34.4231 29.7 34.1031 30.2C33.7431 30.72 33.2231 31.14 32.5231 31.42V31.46C33.1831 31.64 33.7631 32.04 34.2431 32.66C34.7831 33.34 35.0631 34.12 35.0631 35C35.0631 36.28 34.5831 37.3 33.6631 38.1C32.7231 38.88 31.4031 39.28 29.7031 39.28C27.9831 39.28 26.6631 38.88 25.7431 38.1C24.8031 37.3 24.3431 36.28 24.3431 35C24.3431 34.12 24.6031 33.34 25.1631 32.66C25.6431 32.04 26.2031 31.64 26.8831 31.46V31.42C26.1631 31.14 25.6431 30.72 25.3031 30.2C24.9831 29.7 24.8231 29.08 24.8231 28.36C24.8231 27.28 25.2231 26.38 26.0431 25.66C26.9431 24.84 28.1631 24.44 29.7031 24.44ZM29.7031 26.3C28.8631 26.3 28.1831 26.52 27.6831 26.98C27.2431 27.38 27.0231 27.86 27.0231 28.44C27.0231 29.1 27.2031 29.62 27.5631 30C28.0031 30.46 28.7031 30.7 29.7031 30.7C30.6831 30.7 31.4031 30.46 31.8431 30C32.2031 29.62 32.3831 29.1 32.3831 28.44C32.3831 27.86 32.1631 27.38 31.7231 26.98C31.2031 26.52 30.5431 26.3 29.7031 26.3ZM29.7031 32.44C28.7031 32.44 27.9431 32.68 27.4231 33.16C26.9031 33.62 26.6631 34.24 26.6631 35C26.6631 35.72 26.9231 36.3 27.4631 36.74C27.9831 37.18 28.7231 37.4 29.7031 37.4C30.6631 37.4 31.4231 37.16 31.9831 36.72C32.4831 36.28 32.7431 35.72 32.7431 35C32.7431 34.24 32.4831 33.62 32.0031 33.18C31.4631 32.68 30.7031 32.44 29.7031 32.44ZM40.8153 28.78H42.6953V32.88H46.7953V34.76H42.6953V38.88H40.8153V34.76H36.6953V32.88H40.8153V28.78Z" fill="#FF0F53"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.16675 10H15.8334" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M10.8333 15L15.8333 10" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M10.8333 5L15.8333 10" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 459 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.08331 9.91668L9.91665 4.08334" stroke="#FF4A7B" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.08331 4.08334H9.91665V9.91668" stroke="#FF4A7B" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 367 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.5 6.75C4.5 7.94347 4.97411 9.08807 5.81802 9.93198C6.66193 10.7759 7.80653 11.25 9 11.25C10.1935 11.25 11.3381 10.7759 12.182 9.93198C13.0259 9.08807 13.5 7.94347 13.5 6.75C13.5 5.55653 13.0259 4.41193 12.182 3.56802C11.3381 2.72411 10.1935 2.25 9 2.25C7.80653 2.25 6.66193 2.72411 5.81802 3.56802C4.97411 4.41193 4.5 5.55653 4.5 6.75Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M9 11.25L11.55 15.6675L12.7485 13.2427L15.447 13.4167L12.897 9" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M5.10127 9L2.55127 13.4175L5.24977 13.2427L6.44827 15.6667L8.99827 11.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 863 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 899 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.83325 2.91667C5.83325 2.60725 5.95617 2.3105 6.17496 2.09171C6.39375 1.87292 6.6905 1.75 6.99992 1.75C7.30934 1.75 7.60608 1.87292 7.82488 2.09171C8.04367 2.3105 8.16659 2.60725 8.16659 2.91667C8.83649 3.23343 9.40757 3.72652 9.81861 4.34309C10.2297 4.95966 10.4652 5.67646 10.4999 6.41667V8.16667C10.5438 8.52933 10.6723 8.87662 10.8749 9.18057C11.0775 9.48453 11.3487 9.73666 11.6666 9.91667H2.33325C2.65113 9.73666 2.92231 9.48453 3.12494 9.18057C3.32758 8.87662 3.45602 8.52933 3.49992 8.16667V6.41667C3.53466 5.67646 3.77018 4.95966 4.18122 4.34309C4.59227 3.72652 5.16335 3.23343 5.83325 2.91667Z" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#141414"/>
|
||||
<path d="M5.25 9.91663V10.5C5.25 10.9641 5.43437 11.4092 5.76256 11.7374C6.09075 12.0656 6.53587 12.25 7 12.25C7.46413 12.25 7.90925 12.0656 8.23744 11.7374C8.56563 11.4092 8.75 10.9641 8.75 10.5V9.91663" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#141414"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 5.83333V17.5L10 14.1667L5 17.5V5.83333C5 4.94928 5.35119 4.10143 5.97631 3.47631C6.60143 2.85119 7.44928 2.5 8.33333 2.5H11.6667C12.5507 2.5 13.3986 2.85119 14.0237 3.47631C14.6488 4.10143 15 4.94928 15 5.83333Z" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 416 B |
@@ -0,0 +1,9 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.33398 8.33301V13.333" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M16.666 8.33301V13.333" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M5.83398 7.49967H14.1673M5.83398 7.49967V14.1663C5.83398 14.3874 5.92178 14.5993 6.07806 14.7556C6.23434 14.9119 6.4463 14.9997 6.66732 14.9997H13.334C13.555 14.9997 13.767 14.9119 13.9232 14.7556C14.0795 14.5993 14.1673 14.3874 14.1673 14.1663V7.49967M5.83398 7.49967C5.83398 6.39461 6.27297 5.3348 7.05437 4.5534C7.83577 3.77199 8.89558 3.33301 10.0007 3.33301C11.1057 3.33301 12.1655 3.77199 12.9469 4.5534C13.7283 5.3348 14.1673 6.39461 14.1673 7.49967" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M6.66602 2.5L7.49935 4.16667" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M13.3333 2.5L12.5 4.16667" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M7.5 15V17.5" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M12.5 15V17.5" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.90565 5.84005C4.22565 5.84005 3.33398 8.53172 3.33398 10.7734C3.33398 13.4642 5.11982 17.5001 6.90565 17.5001C7.87648 17.4584 8.40482 17.0517 9.58398 17.0517C10.7557 17.0517 10.9232 17.5001 12.2623 17.5001C13.6015 17.5001 15.834 14.8092 15.834 13.0159C15.809 13.0067 13.6265 12.6542 13.6015 10.3242C13.5848 8.37839 15.759 7.67505 15.834 7.63422C14.9207 6.29589 13.199 5.87339 12.709 5.84005C11.4298 5.74005 10.1823 6.73755 9.58398 6.73755C8.97648 6.73755 7.88732 5.84005 6.90565 5.84005Z" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M10 3.33366C10.442 3.33366 10.866 3.15806 11.1785 2.8455C11.4911 2.53294 11.6667 2.10902 11.6667 1.66699C11.2246 1.66699 10.8007 1.84259 10.4882 2.15515C10.1756 2.46771 10 2.89163 10 3.33366Z" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 980 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.66675 5.33317C2.66675 4.62593 2.9477 3.94765 3.4478 3.44755C3.94789 2.94746 4.62617 2.6665 5.33341 2.6665H10.6667C11.374 2.6665 12.0523 2.94746 12.5524 3.44755C13.0525 3.94765 13.3334 4.62593 13.3334 5.33317V10.6665C13.3334 11.3737 13.0525 12.052 12.5524 12.5521C12.0523 13.0522 11.374 13.3332 10.6667 13.3332H5.33341C4.62617 13.3332 3.94789 13.0522 3.4478 12.5521C2.9477 12.052 2.66675 11.3737 2.66675 10.6665V5.33317Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
<path d="M6 8C6 8.53043 6.21071 9.03914 6.58579 9.41421C6.96086 9.78929 7.46957 10 8 10C8.53043 10 9.03914 9.78929 9.41421 9.41421C9.78929 9.03914 10 8.53043 10 8C10 7.46957 9.78929 6.96086 9.41421 6.58579C9.03914 6.21071 8.53043 6 8 6C7.46957 6 6.96086 6.21071 6.58579 6.58579C6.21071 6.96086 6 7.46957 6 8Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
<path d="M11 5V5.00667" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 6.6665L7.33333 9.33317L8 9.99984M8 9.99984L11.3333 13.3332L14 2.6665L2 7.33317L4.66667 8.6665L6 12.6665L8 9.99984Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 321 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14 5.278V7.96733C12.7907 7.84736 11.6376 7.39743 10.6667 6.66667V9.66667C10.6665 10.4808 10.437 11.2783 10.0045 11.968C9.57194 12.6577 8.95388 13.2116 8.22111 13.5662C7.48833 13.9209 6.67047 14.0619 5.86123 13.9732C5.05199 13.8846 4.2841 13.5697 3.64554 13.0648C3.00697 12.5598 2.52356 11.8852 2.25068 11.1182C1.97781 10.3512 1.92651 9.52286 2.10266 8.72806C2.27881 7.93326 2.67529 7.20416 3.24667 6.62428C3.81805 6.0444 4.54122 5.6372 5.33333 5.44933V8.33333C5.10606 8.50379 4.92613 8.72951 4.81063 8.98907C4.69513 9.24862 4.64789 9.5334 4.6734 9.81634C4.69892 10.0993 4.79633 10.371 4.9564 10.6057C5.11647 10.8404 5.33388 11.0303 5.58798 11.1574C5.84208 11.2844 6.12444 11.3444 6.40825 11.3316C6.69206 11.3189 6.96789 11.2338 7.20955 11.0844C7.45122 10.9351 7.65069 10.7264 7.78904 10.4783C7.92738 10.2301 8 9.95076 8 9.66667V2H10.722C10.8608 2.81894 11.2509 3.57442 11.8383 4.16175C12.4256 4.74909 13.1811 5.13919 14 5.278Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.66675 2.6665L10.4887 13.3332H13.3334L5.51142 2.6665H2.66675Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
<path d="M2.66675 13.3332L7.17875 8.82117M8.81875 7.18117L13.3334 2.6665" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 429 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.33325 5.33317C1.33325 4.62593 1.6142 3.94765 2.1143 3.44755C2.6144 2.94746 3.29267 2.6665 3.99992 2.6665H11.9999C12.7072 2.6665 13.3854 2.94746 13.8855 3.44755C14.3856 3.94765 14.6666 4.62593 14.6666 5.33317V10.6665C14.6666 11.3737 14.3856 12.052 13.8855 12.5521C13.3854 13.0522 12.7072 13.3332 11.9999 13.3332H3.99992C3.29267 13.3332 2.6144 13.0522 2.1143 12.5521C1.6142 12.052 1.33325 11.3737 1.33325 10.6665V5.33317Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
<path d="M6.66675 6L10.0001 8L6.66675 10V6Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 759 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.75 7C1.75 7.68944 1.8858 8.37213 2.14963 9.00909C2.41347 9.64605 2.80018 10.2248 3.28769 10.7123C3.7752 11.1998 4.35395 11.5865 4.99091 11.8504C5.62787 12.1142 6.31056 12.25 7 12.25C7.68944 12.25 8.37213 12.1142 9.00909 11.8504C9.64605 11.5865 10.2248 11.1998 10.7123 10.7123C11.1998 10.2248 11.5865 9.64605 11.8504 9.00909C12.1142 8.37213 12.25 7.68944 12.25 7C12.25 6.31056 12.1142 5.62787 11.8504 4.99091C11.5865 4.35395 11.1998 3.7752 10.7123 3.28769C10.2248 2.80018 9.64605 2.41347 9.00909 2.14963C8.37213 1.8858 7.68944 1.75 7 1.75C6.31056 1.75 5.62787 1.8858 4.99091 2.14963C4.35395 2.41347 3.7752 2.80018 3.28769 3.28769C2.80018 3.7752 2.41347 4.35395 2.14963 4.99091C1.8858 5.62787 1.75 6.31056 1.75 7Z" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#FFC000"/>
|
||||
<path d="M5.25 6.99967L6.41667 8.16634L8.75 5.83301" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#FFC000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="17" height="17" rx="8.5" stroke="white" stroke-opacity="0.24"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 196 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="18" height="18" rx="9" fill="#FF0F53"/>
|
||||
<path d="M5.5 9L8 11.5L13 6.5" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 251 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.75 5.5L8.25 11L13.75 16.5" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 230 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 4L10 8L6 12" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 217 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 8C2 8.78793 2.15519 9.56815 2.45672 10.2961C2.75825 11.0241 3.20021 11.6855 3.75736 12.2426C4.31451 12.7998 4.97595 13.2417 5.7039 13.5433C6.43185 13.8448 7.21207 14 8 14C8.78793 14 9.56815 13.8448 10.2961 13.5433C11.0241 13.2417 11.6855 12.7998 12.2426 12.2426C12.7998 11.6855 13.2417 11.0241 13.5433 10.2961C13.8448 9.56815 14 8.78793 14 8C14 6.4087 13.3679 4.88258 12.2426 3.75736C11.1174 2.63214 9.5913 2 8 2C6.4087 2 4.88258 2.63214 3.75736 3.75736C2.63214 4.88258 2 6.4087 2 8Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8 4.66602V7.99935L10 9.99935" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 818 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.5 4.5L4.5 13.5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
<path d="M4.5 4.5L13.5 13.5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#B9BBC2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 331 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="18" cy="18" r="14" stroke="white" stroke-width="2"/>
|
||||
<path d="M23.0903 12.9082L12.908 23.0905" stroke="white" stroke-width="2.25" stroke-linecap="round"/>
|
||||
<path d="M23.0922 23.0893L12.9098 12.907" stroke="white" stroke-width="2.25" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 372 B |
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 9.667C7 8.95967 7.28099 8.28131 7.78115 7.78115C8.28131 7.28099 8.95967 7 9.667 7H18.333C18.6832 7 19.03 7.06898 19.3536 7.20301C19.6772 7.33704 19.9712 7.53349 20.2189 7.78115C20.4665 8.0288 20.663 8.32281 20.797 8.64638C20.931 8.96996 21 9.31676 21 9.667V18.333C21 18.6832 20.931 19.03 20.797 19.3536C20.663 19.6772 20.4665 19.9712 20.2189 20.2189C19.9712 20.4665 19.6772 20.663 19.3536 20.797C19.03 20.931 18.6832 21 18.333 21H9.667C9.31676 21 8.96996 20.931 8.64638 20.797C8.32281 20.663 8.0288 20.4665 7.78115 20.2189C7.53349 19.9712 7.33704 19.6772 7.20301 19.3536C7.06898 19.03 7 18.6832 7 18.333V9.667Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M4.012 16.737C3.70534 16.5622 3.45027 16.3095 3.27258 16.0045C3.09488 15.6995 3.00085 15.353 3 15V5C3 3.9 3.9 3 5 3H15C15.75 3 16.158 3.385 16.5 4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 12.75V14.25C3 14.6478 3.15804 15.0294 3.43934 15.3107C3.72064 15.592 4.10218 15.75 4.5 15.75H13.5C13.8978 15.75 14.2794 15.592 14.5607 15.3107C14.842 15.0294 15 14.6478 15 14.25V12.75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M5.25 8.25L9 12L12.75 8.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M9 3V12" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 604 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.75 7C1.75 7.68944 1.8858 8.37213 2.14963 9.00909C2.41347 9.64605 2.80018 10.2248 3.28769 10.7123C3.7752 11.1998 4.35395 11.5865 4.99091 11.8504C5.62787 12.1142 6.31056 12.25 7 12.25C7.68944 12.25 8.37213 12.1142 9.00909 11.8504C9.64605 11.5865 10.2248 11.1998 10.7123 10.7123C11.1998 10.2248 11.5865 9.64605 11.8504 9.00909C12.1142 8.37213 12.25 7.68944 12.25 7C12.25 5.60761 11.6969 4.27226 10.7123 3.28769C9.72774 2.30312 8.39239 1.75 7 1.75C5.60761 1.75 4.27226 2.30312 3.28769 3.28769C2.30312 4.27226 1.75 5.60761 1.75 7Z" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#FF4D4F"/>
|
||||
<path d="M7 4.66602V6.99935" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#FF4D4F"/>
|
||||
<path d="M7 9.33398H7.00583" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#FF4D4F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 967 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15.75 9.75V14.25C15.75 14.6478 15.592 15.0294 15.3107 15.3107C15.0294 15.592 14.6478 15.75 14.25 15.75H3.75C3.35218 15.75 2.97064 15.592 2.68934 15.3107C2.40804 15.0294 2.25 14.6478 2.25 14.25V3.75C2.25 3.35218 2.40804 2.97064 2.68934 2.68934C2.97064 2.40804 3.35218 2.25 3.75 2.25H8.25" stroke="#FF4A7B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11.25 2.25H15.75V6.75" stroke="#FF4A7B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.5 10.5L15.75 2.25" stroke="#FF4A7B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 720 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.0725 13.0725C11.9313 13.9425 10.515 14.4375 9 14.4375C3.75 14.4375 0.75 9 0.75 9C1.6125 7.4025 2.835 6.075 4.275 5.115M7.4325 3.27C7.9425 3.1425 8.4675 3.075 9 3.075C14.25 3.075 17.25 9 17.25 9C16.7925 9.8475 16.2375 10.635 15.6075 11.34M0.75 0.75L17.25 17.25" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.6125 7.61255C7.24773 7.97732 7.04276 8.47206 7.04276 8.98755C7.04276 9.50303 7.24773 9.99777 7.6125 10.3625C7.97727 10.7273 8.47201 10.9323 8.9875 10.9323C9.50298 10.9323 9.99772 10.7273 10.3625 10.3625" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 763 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 9C7.5 9.39782 7.65804 9.77936 7.93934 10.0607C8.22064 10.342 8.60218 10.5 9 10.5C9.39782 10.5 9.77936 10.342 10.0607 10.0607C10.342 9.77936 10.5 9.39782 10.5 9C10.5 8.60218 10.342 8.22064 10.0607 7.93934C9.77936 7.65804 9.39782 7.5 9 7.5C8.60218 7.5 8.22064 7.65804 7.93934 7.93934C7.65804 8.22064 7.5 8.60218 7.5 9Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M15.75 9C13.95 12 11.7 13.5 9 13.5C6.3 13.5 4.05 12 2.25 9C4.05 6 6.3 4.5 9 4.5C11.7 4.5 13.95 6 15.75 9Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 721 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.16675 10H15.8334" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M10.8333 15L15.8333 10" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M10.8333 5L15.8333 10" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 459 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M30 11.6667V35L20 28.3333L10 35V11.6667C10 9.89856 10.7024 8.20286 11.9526 6.95262C13.2029 5.70238 14.8986 5 16.6667 5H23.3333C25.1014 5 26.7971 5.70238 28.0474 6.95262C29.2976 8.20286 30 9.89856 30 11.6667Z" stroke-width="3.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 410 B |
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 3.99935C8 4.35297 8.14048 4.69211 8.39052 4.94216C8.64057 5.19221 8.97971 5.33268 9.33333 5.33268C9.68696 5.33268 10.0261 5.19221 10.2761 4.94216C10.5262 4.69211 10.6667 4.35297 10.6667 3.99935C10.6667 3.64573 10.5262 3.30659 10.2761 3.05654C10.0261 2.80649 9.68696 2.66602 9.33333 2.66602C8.97971 2.66602 8.64057 2.80649 8.39052 3.05654C8.14048 3.30659 8 3.64573 8 3.99935Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M2.66699 4H8.00033" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M10.667 4H13.3337" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M4 7.99935C4 8.35297 4.14048 8.69211 4.39052 8.94216C4.64057 9.19221 4.97971 9.33268 5.33333 9.33268C5.68696 9.33268 6.02609 9.19221 6.27614 8.94216C6.52619 8.69211 6.66667 8.35297 6.66667 7.99935C6.66667 7.64573 6.52619 7.30659 6.27614 7.05654C6.02609 6.80649 5.68696 6.66602 5.33333 6.66602C4.97971 6.66602 4.64057 6.80649 4.39052 7.05654C4.14048 7.30659 4 7.64573 4 7.99935Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M2.66699 8H4.00033" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M6.66699 8H13.3337" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M10 11.9993C10 12.353 10.1405 12.6921 10.3905 12.9422C10.6406 13.1922 10.9797 13.3327 11.3333 13.3327C11.687 13.3327 12.0261 13.1922 12.2761 12.9422C12.5262 12.6921 12.6667 12.353 12.6667 11.9993C12.6667 11.6457 12.5262 11.3066 12.2761 11.0565C12.0261 10.8065 11.687 10.666 11.3333 10.666C10.9797 10.666 10.6406 10.8065 10.3905 11.0565C10.1405 11.3066 10 11.6457 10 11.9993Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M2.66699 12H10.0003" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M12.667 12H13.3337" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 8C2 8.78793 2.15519 9.56815 2.45672 10.2961C2.75825 11.0241 3.20021 11.6855 3.75736 12.2426C4.31451 12.7998 4.97595 13.2417 5.7039 13.5433C6.43185 13.8448 7.21207 14 8 14C8.78793 14 9.56815 13.8448 10.2961 13.5433C11.0241 13.2417 11.6855 12.7998 12.2426 12.2426C12.7998 11.6855 13.2417 11.0241 13.5433 10.2961C13.8448 9.56815 14 8.78793 14 8C14 6.4087 13.3679 4.88258 12.2426 3.75736C11.1174 2.63214 9.5913 2 8 2C6.4087 2 4.88258 2.63214 3.75736 3.75736C2.63214 4.88258 2 6.4087 2 8Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8 10.666V10.6727" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8 8.66651C8.29983 8.66741 8.59122 8.56722 8.8271 8.38213C9.06299 8.19704 9.2296 7.93784 9.30003 7.64639C9.37046 7.35495 9.34061 7.04827 9.21529 6.77588C9.08996 6.5035 8.87649 6.2813 8.60933 6.14518C8.34412 6.00932 8.04074 5.96719 7.74854 6.02566C7.45634 6.08412 7.19252 6.23973 7 6.46718" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.66675 5.50008C3.66675 5.01385 3.8599 4.54754 4.20372 4.20372C4.54754 3.8599 5.01385 3.66675 5.50008 3.66675H16.5001C16.9863 3.66675 17.4526 3.8599 17.7964 4.20372C18.1403 4.54754 18.3334 5.01385 18.3334 5.50008V16.5001C18.3334 16.9863 18.1403 17.4526 17.7964 17.7964C17.4526 18.1403 16.9863 18.3334 16.5001 18.3334H5.50008C5.01385 18.3334 4.54754 18.1403 4.20372 17.7964C3.8599 17.4526 3.66675 16.9863 3.66675 16.5001V5.50008Z" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M7.33325 3.66675V18.3334" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M14.6667 3.66675V18.3334" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M3.66675 7.33325H7.33341" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M3.66675 14.6667H7.33341" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M3.66675 11H18.3334" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M14.6667 7.33325H18.3334" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M14.6667 14.6667H18.3334" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,10 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.66602 5.50008C3.66602 5.01385 3.85917 4.54754 4.20299 4.20372C4.5468 3.8599 5.01312 3.66675 5.49935 3.66675H16.4993C16.9856 3.66675 17.4519 3.8599 17.7957 4.20372C18.1395 4.54754 18.3327 5.01385 18.3327 5.50008V16.5001C18.3327 16.9863 18.1395 17.4526 17.7957 17.7964C17.4519 18.1403 16.9856 18.3334 16.4993 18.3334H5.49935C5.01312 18.3334 4.5468 18.1403 4.20299 17.7964C3.85917 17.4526 3.66602 16.9863 3.66602 16.5001V5.50008Z" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M7.33398 3.66675V18.3334" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M14.666 3.66675V18.3334" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M3.66602 7.33325H7.33268" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M3.66602 14.6667H7.33268" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M3.66602 11H18.3327" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M14.666 7.33325H18.3327" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M14.666 14.6667H18.3327" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.25 9V14.25C14.25 14.6478 14.092 15.0294 13.8107 15.3107C13.5294 15.592 13.1478 15.75 12.75 15.75H5.25C4.85218 15.75 4.47064 15.592 4.18934 15.3107C3.90804 15.0294 3.75 14.6478 3.75 14.25V9" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15 4.5H3C2.58579 4.5 2.25 4.83579 2.25 5.25V6.75C2.25 7.16421 2.58579 7.5 3 7.5H15C15.4142 7.5 15.75 7.16421 15.75 6.75V5.25C15.75 4.83579 15.4142 4.5 15 4.5Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 15.75V4.5" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 4.5H6.375C5.87772 4.5 5.40081 4.30246 5.04917 3.95083C4.69754 3.59919 4.5 3.12228 4.5 2.625C4.5 2.12772 4.69754 1.65081 5.04917 1.29917C5.40081 0.947544 5.87772 0.75 6.375 0.75C8.25 0.75 9 4.5 9 4.5Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 4.5H11.625C12.1223 4.5 12.5992 4.30246 12.9508 3.95083C13.3025 3.59919 13.5 3.12228 13.5 2.625C13.5 2.12772 13.3025 1.65081 12.9508 1.29917C12.5992 0.947544 12.1223 0.75 11.625 0.75C9.75 0.75 9 4.5 9 4.5Z" stroke="#7C7C86" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,7 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.3333 11V18.3333C18.3333 18.8196 18.1402 19.2859 17.7964 19.6297C17.4526 19.9735 16.9862 20.1667 16.5 20.1667H5.5C5.01377 20.1667 4.54745 19.9735 4.20367 19.6297C3.85988 19.2859 3.66667 18.8196 3.66667 18.3333V11" stroke="white" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M19.25 6.41699H2.75C2.24365 6.41699 1.83333 6.82731 1.83333 7.33366V9.16699C1.83333 9.67334 2.24365 10.0837 2.75 10.0837H19.25C19.7563 10.0837 20.1667 9.67334 20.1667 9.16699V7.33366C20.1667 6.82731 19.7563 6.41699 19.25 6.41699Z" stroke="white" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11 20.1667V6.41699" stroke="white" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11 6.41699H7.33333C6.8471 6.41699 6.38078 6.22378 6.037 5.88C5.69321 5.53621 5.5 5.06989 5.5 4.58366C5.5 4.09742 5.69321 3.6311 6.037 3.28732C6.38078 2.94353 6.8471 2.75033 7.33333 2.75033C10.0833 2.75033 11 6.41699 11 6.41699Z" stroke="white" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M11 6.41699H14.6667C15.1529 6.41699 15.6192 6.22378 15.963 5.88C16.3068 5.53621 16.5 5.06989 16.5 4.58366C16.5 4.09742 16.3068 3.6311 15.963 3.28732C15.6192 2.94353 15.1529 2.75033 14.6667 2.75033C11.9167 2.75033 11 6.41699 11 6.41699Z" stroke="white" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.2501 10.4771L10.0001 16.6671L3.75009 10.4771C3.33784 10.0759 3.01312 9.59374 2.79638 9.06092C2.57963 8.52811 2.47556 7.95618 2.4907 7.38117C2.50585 6.80615 2.63989 6.2405 2.88439 5.71984C3.12888 5.19917 3.47853 4.73477 3.91133 4.35587C4.34412 3.97698 4.85068 3.6918 5.3991 3.51829C5.94752 3.34479 6.52593 3.28671 7.09789 3.34773C7.66986 3.40874 8.223 3.58752 8.72248 3.87281C9.22196 4.1581 9.65696 4.54373 10.0001 5.00539C10.3447 4.54708 10.7802 4.16483 11.2793 3.88256C11.7785 3.6003 12.3305 3.4241 12.9009 3.36499C13.4712 3.30588 14.0477 3.36514 14.5941 3.53905C15.1405 3.71296 15.6451 3.99779 16.0764 4.37569C16.5077 4.7536 16.8563 5.21646 17.1004 5.7353C17.3446 6.25414 17.479 6.81779 17.4953 7.39098C17.5117 7.96417 17.4095 8.53455 17.1952 9.06643C16.9809 9.59831 16.6592 10.0802 16.2501 10.4821" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1005 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 8C2 8.78793 2.15519 9.56815 2.45672 10.2961C2.75825 11.0241 3.20021 11.6855 3.75736 12.2426C4.31451 12.7998 4.97595 13.2417 5.7039 13.5433C6.43185 13.8448 7.21207 14 8 14C8.78793 14 9.56815 13.8448 10.2961 13.5433C11.0241 13.2417 11.6855 12.7998 12.2426 12.2426C12.7998 11.6855 13.2417 11.0241 13.5433 10.2961C13.8448 9.56815 14 8.78793 14 8C14 6.4087 13.3679 4.88258 12.2426 3.75736C11.1174 2.63214 9.5913 2 8 2C6.4087 2 4.88258 2.63214 3.75736 3.75736C2.63214 4.88258 2 6.4087 2 8Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8 10.666V10.6727" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8 8.66651C8.29983 8.66741 8.59122 8.56722 8.8271 8.38213C9.06299 8.19704 9.2296 7.93784 9.30003 7.64639C9.37046 7.35495 9.34061 7.04827 9.21529 6.77588C9.08996 6.5035 8.87649 6.2813 8.60933 6.14518C8.34412 6.00932 8.04074 5.96719 7.74854 6.02566C7.45634 6.08412 7.19252 6.23973 7 6.46718" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.339 5.70643C18.7769 5.95663 18.929 6.51443 18.6788 6.95233L17.2805 9.39933C19.8373 11.0022 21.6296 13.727 21.9457 16.9107C21.999 17.448 21.577 17.9141 21.0369 17.9141H2.86345C2.32345 17.9141 1.90141 17.448 1.95475 16.9107C2.25696 13.8667 3.90855 11.2422 6.28765 9.61678L4.7651 6.95233C4.51488 6.51443 4.66701 5.95663 5.1049 5.70643C5.5428 5.45618 6.1006 5.60833 6.35085 6.04623L7.88305 8.72758C9.1291 8.17513 10.5061 7.86908 11.9502 7.86908C13.2534 7.86908 14.5021 8.11838 15.6491 8.57308L17.093 6.04623C17.3433 5.60833 17.9011 5.45618 18.339 5.70643ZM7.8409 12.8916C7.3366 12.8916 6.92775 13.3004 6.92775 13.8047C6.92775 14.3091 7.3366 14.7179 7.8409 14.7179C8.3453 14.7179 8.7541 14.3091 8.7541 13.8047C8.7541 13.3004 8.3453 12.8916 7.8409 12.8916ZM16.0595 12.8916C15.5552 12.8916 15.1464 13.3004 15.1464 13.8047C15.1464 14.3091 15.5552 14.7179 16.0595 14.7179C16.5639 14.7179 16.9727 14.3091 16.9727 13.8047C16.9727 13.3004 16.5639 12.8916 16.0595 12.8916Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.0002 6.50215V16.6992C21.0024 16.8964 20.9646 17.092 20.8891 17.2742C20.8136 17.4565 20.702 17.6214 20.561 17.7593C20.4238 17.9014 20.2589 18.0135 20.0765 18.0891C19.8941 18.1647 19.6981 18.202 19.5008 18.1986H14.3978C14.4017 18.446 14.453 18.6904 14.549 18.9186C14.6371 19.1455 14.7368 19.3678 14.8478 19.5846C14.9258 19.7083 14.9772 19.8468 14.999 19.9914C14.9975 20.1504 14.9338 20.3025 14.8213 20.4149C14.7089 20.5273 14.5568 20.5912 14.3978 20.5926H9.60076C9.44171 20.5912 9.28961 20.5273 9.17716 20.4149C9.06476 20.3025 9.00096 20.1504 8.99956 19.9914C9.01971 19.8444 9.07191 19.7037 9.15256 19.5792C9.26396 19.3658 9.36371 19.1464 9.45136 18.9221C9.54871 18.6907 9.60006 18.4425 9.60256 18.1914H4.50134C4.30404 18.1941 4.10826 18.1566 3.92596 18.0811C3.74367 18.0055 3.5787 17.8936 3.44114 17.7521C3.29973 17.6159 3.18767 17.4523 3.11183 17.2712C3.03599 17.0901 2.99798 16.8954 3.00014 16.6992V6.50215C2.9974 6.30485 3.03494 6.1091 3.11045 5.9268C3.18597 5.7445 3.29788 5.5795 3.43934 5.44195C3.57599 5.29955 3.74046 5.18675 3.92253 5.1106C4.1046 5.0344 4.30039 4.99649 4.49774 4.99915H19.4972C19.8956 4.99963 20.2776 5.15815 20.5594 5.4399C20.8412 5.72165 20.9997 6.1037 21.0002 6.50215ZM19.7978 14.3034V6.50215C19.7978 6.46295 19.79 6.42415 19.7748 6.388C19.7597 6.3518 19.7375 6.31905 19.7096 6.29155C19.682 6.26315 19.649 6.24065 19.6125 6.2255C19.5759 6.2103 19.5367 6.2028 19.4972 6.20335H4.50134C4.46213 6.20335 4.42332 6.21115 4.38716 6.22625C4.351 6.2414 4.31822 6.2636 4.29074 6.29155C4.26254 6.31885 4.24019 6.3516 4.22503 6.3878C4.20986 6.424 4.20221 6.4629 4.20254 6.50215V14.3015C4.20228 14.3411 4.20995 14.3802 4.2251 14.4167C4.24025 14.4532 4.26257 14.4862 4.29074 14.514C4.31804 14.5422 4.35078 14.5645 4.38698 14.5796C4.42318 14.5948 4.46209 14.6025 4.50134 14.6021H19.499C19.5385 14.6024 19.5776 14.5948 19.6141 14.5796C19.6506 14.5645 19.6837 14.5421 19.7114 14.514C19.7395 14.487 19.7618 14.4546 19.777 14.4187C19.7922 14.3828 19.7998 14.3441 19.7996 14.3052L19.7978 14.3034Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M35 4C38.0375 4 40.5 6.4625 40.5 9.5V38.5C40.5 41.5375 38.0375 44 35 44H13C9.9625 44 7.5 41.5375 7.5 38.5V9.5C7.5 6.4625 9.9625 4 13 4H35ZM28 36.5H20C19.6022 36.5 19.2206 36.658 18.9393 36.9393C18.658 37.2206 18.5 37.6021 18.5 38C18.5 38.3978 18.658 38.7793 18.9393 39.0606C19.2206 39.3419 19.6022 39.5 20 39.5H28C28.3978 39.5 28.7794 39.3419 29.0607 39.0606C29.342 38.7793 29.5 38.3978 29.5 38C29.5 37.6021 29.342 37.2206 29.0607 36.9393C28.7794 36.658 28.3978 36.5 28 36.5ZM35 7H13C12.6717 7 12.3466 7.06467 12.0433 7.1903C11.74 7.31594 11.4644 7.50009 11.2322 7.73223C11.0001 7.96438 10.8159 8.23998 10.6903 8.54329C10.5647 8.84661 10.5 9.1717 10.5 9.5V32H37.5V9.5C37.5 9.1717 37.4353 8.84661 37.3097 8.54329C37.1841 8.23998 36.9999 7.96438 36.7678 7.73223C36.5356 7.50009 36.26 7.31594 35.9567 7.1903C35.6534 7.06467 35.3283 7 35 7Z" fill="#EEEEEE"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 967 B |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 122 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.5408 16.0097C19.5183 16.0772 19.1662 17.2955 18.3011 18.5487C17.5519 19.6297 16.7769 20.7198 15.5541 20.7389C14.3504 20.7603 13.9657 20.0268 12.5921 20.0268C11.2209 20.0268 10.79 20.7198 9.6527 20.7603C8.47715 20.8075 7.57155 19.5791 6.82125 18.5037C5.28345 16.3 4.10115 12.2592 5.6873 9.55152C6.46915 8.19597 7.8719 7.33537 9.39955 7.31062C10.5515 7.28927 11.6472 8.08457 12.3469 8.08457C13.0657 8.08457 14.3909 7.12277 15.7892 7.26677C16.3742 7.29152 18.012 7.50862 19.0672 9.02617C18.9851 9.08242 17.1076 10.1803 17.129 12.4167C17.1571 15.1154 19.5161 16.0007 19.5408 16.0097ZM14.8746 5.82012C15.5045 5.05742 15.9197 4.00897 15.8105 2.96729C14.9106 3.00216 13.8127 3.56012 13.1614 4.31945C12.5843 4.98091 12.0769 6.04622 12.2187 7.07552C13.2187 7.13517 14.2469 6.55582 14.8735 5.82012" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 920 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M25.2998 21.0001L30.4998 18.8001C31.8998 18.2001 32.5998 16.6001 31.9998 15.2001C31.3998 13.8001 29.6998 13.2001 28.2998 13.8001L22.9998 15.9001C21.5998 16.5001 20.8998 18.1001 21.4998 19.5001C22.1998 20.9001 23.8998 21.6001 25.2998 21.0001ZM42.7998 17.0001L37.9998 6.1001C37.5998 5.2001 36.4998 4.7001 35.4998 5.1001L23.3998 10.2001C22.3998 10.6001 21.9998 11.7001 22.3998 12.6001L22.8998 13.8001L27.3998 11.9001C29.7998 10.9001 32.5998 12.0001 33.5998 14.3001C34.5998 16.6001 33.4998 19.3001 31.1998 20.4001L26.6998 22.3001L27.1998 23.5001C27.5998 24.4001 28.6998 24.9001 29.6998 24.5001L41.7998 19.4001C42.7998 19.0001 43.2998 17.9001 42.7998 17.0001ZM15.5998 32.0001C16.9998 32.7001 18.6998 32.2001 19.3998 30.8001L21.9998 25.9001C22.6998 24.5001 22.1998 22.9001 20.7998 22.2001C19.3998 21.5001 17.6998 22.0001 16.9998 23.4001L14.3998 28.3001C13.6998 29.7001 14.2998 31.4001 15.5998 32.0001ZM24.4998 28.0001L23.2998 27.4001L21.0998 31.6001C19.8998 33.9001 17.0998 34.8001 14.7998 33.6001C12.4998 32.4001 11.5998 29.7001 12.7998 27.4001L14.9998 23.2001L13.7998 22.6001C12.8998 22.1001 11.6998 22.5001 11.2998 23.4001L5.29985 34.9001C4.79985 35.8001 5.19985 36.9001 6.09985 37.4001L16.8998 42.8001C17.7998 43.3001 18.9998 42.9001 19.3998 42.0001L25.3998 30.5001C25.7998 29.6001 25.4998 28.5001 24.4998 28.0001Z" fill="#FF951C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.75357 10.8504L7.75117 10.8589L7.75477 10.8619L7.75357 10.8504ZM18.0796 7.1729C17.3967 6.7908 16.4325 5.2013 16.4434 4.7204C16.4901 2.6912 15.1262 2.3941 14.4439 2.4498C12.9308 2.5729 11.9811 2.1193 11.2625 1.36C10.5439 0.600097 9.89927 0.632898 9.89927 0.632898C9.38617 0.632898 8.97507 0.821498 8.44497 1.4504C7.61417 2.4359 6.29877 2.4711 5.89967 2.4492C5.50127 2.428 4.56367 2.3449 3.99117 2.8131C3.41867 3.2813 3.40417 4.0927 3.26407 4.9933C3.12397 5.8933 2.18887 6.7817 1.71887 7.1729C1.24887 7.564 0.973568 7.8697 0.900768 8.2627C0.827968 8.6562 0.699368 8.7945 1.44657 10.0795C2.28227 11.5168 1.84987 12.5368 1.71947 13.5308C1.58907 14.5254 2.18277 15.1828 3.44667 15.529C4.71057 15.8753 5.52677 17.178 5.62807 17.4363C5.72937 17.6947 6.38127 18.6899 6.99137 18.799C7.78337 18.9403 8.29527 18.3878 8.80947 18.2538C9.32317 18.1198 10.4518 18.0021 11.0819 18.3448C13.0232 19.4 13.8425 18.3199 14.0814 17.6183C14.3385 16.8627 15.307 15.7892 16.3538 15.5291C18.6826 14.9512 17.9554 12.9111 17.899 12.7134C17.8426 12.5157 17.6728 10.8304 18.2629 10.17C19.4994 8.7842 18.7625 7.5549 18.0796 7.1729ZM11.8387 10.8528L11.8356 10.8419V10.8553L11.8387 10.8528C12.3384 12.5163 12.4494 13.9422 12.4494 13.9422L9.80037 12.3321C9.79901 12.3328 9.7976 12.3334 9.79617 12.3339L9.78707 12.3309C8.49347 13.2254 7.14297 13.9422 7.14297 13.9422C7.14297 13.9422 7.25267 12.52 7.75117 10.8589C6.52977 9.9426 5.55107 8.6358 5.55107 8.6358L8.46927 8.3708L9.79617 5.452L11.1231 8.3702L14.0413 8.6358C14.0413 8.6356 13.0613 9.9352 11.8387 10.8528Z" fill="#EC237F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.99881 34.0137C8.90963 36.1762 8.08686 39.908 8.08686 39.908C8.08686 39.908 11.8202 39.0865 13.9848 37.9975L15.9714 39.9831C15.7319 40.3942 15.4481 40.789 15.0954 41.1425C12.8655 43.3712 2 45.9906 2 45.9906C2 45.9906 4.62244 35.1334 6.85329 32.9046C7.20584 32.5514 7.59829 32.2658 8.01225 32.0283L9.99881 34.0137ZM29.5866 40.2724L30.9337 31.1603L39.4984 22.5999L39.4537 22.5554C41.5337 20.2933 49.0528 10.73 44.6297 3.37552C37.2728 -1.04326 27.7045 6.47087 25.4396 8.54973L25.418 8.52726L16.8534 17.0873L7.73728 18.4347L6.3088 19.8614L11.0752 24.6262L7.8134 26.1234C6.76322 27.1722 6.77954 28.894 7.85224 29.9667L18.0501 40.159C19.1219 41.2294 20.8453 41.2467 21.8955 40.1978L23.3927 36.9371L28.1582 41.7L29.5866 40.2724ZM34.1989 21.1081C33.1478 22.1585 31.4244 22.1398 30.3526 21.0682L26.9533 17.6709C25.8796 16.5978 25.8624 14.8765 26.9135 13.8265L30.2439 10.4979C31.2952 9.44727 33.0167 9.46465 34.0905 10.5374L37.4898 13.9349C38.5616 15.0062 38.5806 16.7288 37.5294 17.7794L34.1989 21.1081ZM17.963 31.5095L16.5056 30.054L22.4529 24.1098C22.8462 23.7172 23.4927 23.7229 23.8944 24.1244C24.2963 24.5261 24.3029 25.1733 23.9101 25.5654L17.963 31.5095Z" fill="#2ECC71"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38 8.8C33.7 7.7 29.8 6.7 26.4 3.9C25.7 3.3 24.9 3 24 3C23.1 3 22.3 3.3 21.6 3.9C18.7 6.3 15.3 7.2 11.6 8.4C11.1 8.6 10.5 8.7 10 8.8C8.3 9.2 7.1 10.8 7 12.6V13.1L10.2 35.7C10.2 35.9 10.2 36.1 10.3 36.2C10.3 36 10.2 35.9 10.2 35.7C10.3 37 11 38.1 12.1 38.7L22 44.4C22.2 44.5 22.3 44.6 22.5 44.6C23 44.8 23.5 44.9 24 44.9C24.7 44.9 25.4 44.7 26 44.3L35.9 38.6C37 38 37.6 36.8 37.8 35.6C37.8 35.8 37.8 36 37.7 36.1C37.7 35.9 37.8 35.8 37.8 35.6L41 13.1C41.2 11.1 39.9 9.3 38 8.8ZM34.8 35.2V35.3C34.8 35.6 34.6 35.9 34.3 36.1H34.1L24.3 41.8C24.3 42 24.1 42 24 42C23.9 42 23.7 42 23.6 41.9L13.8 36.2H13.6C13.3 36 13.2 35.8 13.1 35.4V35.2L10 12.8C10 12.3 10.3 11.9 10.7 11.8C15.3 10.6 19.8 9.4 23.5 6.3C23.7 6 23.9 6 24 6C24.1 6 24.3 6 24.5 6.2C28.2 9.3 32.6 10.6 37.3 11.7C37.7 11.8 38.1 12.3 38 12.7L34.8 35.2Z" fill="#00D9FF"/>
|
||||
<path d="M34.0004 13.9C30.7004 12.8 27.5004 12.1 24.6004 10.1C24.3004 9.8 23.8004 9.8 23.4004 10.1C20.5004 12.2 17.3004 12.8 14.0004 13.9C13.5004 14.1 13.2004 14.5 13.3004 15L16.0004 33.7C16.0004 34 16.2004 34.2 16.4004 34.4L23.4004 38.2C23.7004 38.4 24.2004 38.4 24.5004 38.2L31.5004 34.4C31.7004 34.2 31.9004 34 31.9004 33.7L34.6004 15C34.7004 14.5 34.4004 14 34.0004 13.9ZM28.9004 22.5L24.0004 27.4C23.4004 28 22.5004 28 21.9004 27.4L19.1004 24.6C18.5004 24 18.5004 23.1 19.1004 22.5C19.7004 21.9 20.6004 21.9 21.2004 22.5L23.0004 24.3L26.9004 20.4C27.5004 19.8 28.4004 19.8 29.0004 20.4C29.5004 21 29.5004 21.9 28.9004 22.5Z" fill="#00D9FF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M28.8609 25.6594C28.3969 25.4391 27.9281 25.2281 27.4406 25.0453C30.5859 22.8938 32.6531 19.2797 32.6531 15.1922C32.6531 8.61094 27.3 3.25781 20.7188 3.25781C14.1375 3.25781 8.78437 8.61094 8.78437 15.1922C8.78437 19.2797 10.8516 22.8938 13.9969 25.0453C7.55156 27.4781 3 33.2859 3 40.0594C3 41.1281 3.83906 41.9953 4.88906 42.0609H4.90781C4.94062 42.0609 4.97813 42.0703 5.01094 42.0703C5.04844 42.0703 5.08125 42.0609 5.11406 42.0609H25.9688C24.6141 40.1484 23.8125 37.8188 23.8125 35.2969C23.8125 31.2984 25.8141 27.7781 28.8609 25.6594ZM12.3563 15.2531C12.3563 10.65 16.0875 6.92344 20.6859 6.92344C25.2844 6.92344 29.0156 10.6547 29.0156 15.2531C29.0156 19.8563 25.2844 23.5828 20.6859 23.5828C16.0875 23.5828 12.3563 19.8563 12.3563 15.2531ZM35.55 25.8469C30.3328 25.8469 26.1 30.075 26.1 35.2969C26.1 40.5188 30.3281 44.7469 35.55 44.7469C40.7719 44.7469 45 40.5141 45 35.2969C45 30.0797 40.7719 25.8469 35.55 25.8469ZM30.8484 29.8641C31.1578 29.7047 31.5328 29.8266 31.6922 30.1313L32.7469 32.1609C32.9062 32.4656 32.7844 32.8453 32.4797 33.0047C32.4094 33.0375 32.3391 33.0609 32.2688 33.0703C32.0156 33.1031 31.7625 32.9766 31.6359 32.7375L30.5813 30.7078C30.4219 30.4031 30.5391 30.0234 30.8484 29.8641ZM28.5563 34.1484C28.2188 34.0688 28.0125 33.7313 28.0922 33.3984C28.1719 33.0609 28.5094 32.8547 28.8422 32.9344L31.0687 33.4594C31.3312 33.5203 31.5141 33.7359 31.5469 33.9891C31.5563 34.0594 31.5516 34.1344 31.5328 34.2094C31.4531 34.5469 31.1156 34.7531 30.7828 34.6734L28.5563 34.1484ZM40.8844 35.7094L35.7891 40.8047C35.3578 41.2359 34.8141 41.5359 34.2141 41.6719C33.9797 41.7234 33.7359 41.7516 33.5016 41.7516C32.6438 41.7516 31.8375 41.4141 31.2281 40.8047C30.6188 40.1953 30.2859 39.3891 30.2859 38.5266C30.2859 37.6641 30.6188 36.8531 31.2281 36.2438C31.3969 36.075 31.6219 35.9813 31.8562 35.9813C32.0953 35.9813 32.3156 36.075 32.4891 36.2438C32.6578 36.4125 32.7516 36.6375 32.7516 36.8719C32.7516 37.1063 32.6578 37.3313 32.4891 37.5C31.9266 38.0625 31.9266 38.9813 32.4891 39.5438C32.7609 39.8156 33.1219 39.9656 33.5062 39.9656C33.6141 39.9656 33.7219 39.9516 33.8297 39.9281C34.0969 39.8719 34.3406 39.7359 34.5328 39.5391L39.6328 34.4438C39.9047 34.1672 40.0547 33.8063 40.0547 33.4219C40.0547 33.0328 39.9047 32.6672 39.6281 32.4C39.3609 32.1281 39 31.9781 38.6156 31.9781C38.5078 31.9781 38.4 31.9922 38.2875 32.0156C38.0203 32.0766 37.7766 32.2078 37.5844 32.4047L35.3016 34.6875C35.1328 34.8563 34.9078 34.95 34.6734 34.95C34.4391 34.95 34.2141 34.8563 34.0453 34.6875C33.8766 34.5188 33.7875 34.2938 33.7875 34.0594C33.7875 33.825 33.8813 33.6 34.05 33.4313L36.3375 31.1484C36.7688 30.7172 37.3172 30.4172 37.9125 30.2859C38.1469 30.2344 38.3906 30.2063 38.6297 30.2063C39.4828 30.2063 40.2891 30.5438 40.8984 31.1531C41.5078 31.7625 41.8406 32.5734 41.8406 33.4313C41.8312 34.2938 41.4938 35.1 40.8844 35.7094Z" fill="#F4184C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.58333 11H2.75L11 2.75L19.25 11H17.4167" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M4.5835 11V17.4167C4.5835 17.9029 4.77665 18.3692 5.12047 18.713C5.46428 19.0568 5.9306 19.25 6.41683 19.25H15.5835C16.0697 19.25 16.536 19.0568 16.8799 18.713C17.2237 18.3692 17.4168 17.9029 17.4168 17.4167V11" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M8.25 19.2493V13.7493C8.25 13.2631 8.44315 12.7968 8.78697 12.453C9.13079 12.1092 9.5971 11.916 10.0833 11.916H11.9167C12.4029 11.916 12.8692 12.1092 13.213 12.453C13.5568 12.7968 13.75 13.2631 13.75 13.7493V19.2493" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 863 B |
@@ -0,0 +1,6 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.75 7.33398H13.7592" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M2.75 5.5C2.75 4.77065 3.03973 4.07118 3.55546 3.55546C4.07118 3.03973 4.77065 2.75 5.5 2.75H16.5C17.2293 2.75 17.9288 3.03973 18.4445 3.55546C18.9603 4.07118 19.25 4.77065 19.25 5.5V16.5C19.25 17.2293 18.9603 17.9288 18.4445 18.4445C17.9288 18.9603 17.2293 19.25 16.5 19.25H5.5C4.77065 19.25 4.07118 18.9603 3.55546 18.4445C3.03973 17.9288 2.75 17.2293 2.75 16.5V5.5Z" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M2.75 14.666L7.33333 10.0827C8.184 9.2641 9.23267 9.2641 10.0833 10.0827L14.6667 14.666" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M12.833 12.8333L13.7497 11.9167C14.6003 11.0981 15.649 11.0981 16.4997 11.9167L19.2497 14.6667" stroke-width="1.83333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.0007 5H5.00065C4.55862 5 4.1347 5.17559 3.82214 5.48816C3.50958 5.80072 3.33398 6.22464 3.33398 6.66667V15C3.33398 15.442 3.50958 15.8659 3.82214 16.1785C4.1347 16.4911 4.55862 16.6667 5.00065 16.6667H13.334C13.776 16.6667 14.1999 16.4911 14.5125 16.1785C14.8251 15.8659 15.0007 15.442 15.0007 15V10" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M9.16602 10.833L16.666 3.33301" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M12.5 3.33301H16.6667V7.49967" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 759 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.75 7C1.75 7.68944 1.8858 8.37213 2.14963 9.00909C2.41347 9.64605 2.80018 10.2248 3.28769 10.7123C3.7752 11.1998 4.35395 11.5865 4.99091 11.8504C5.62787 12.1142 6.31056 12.25 7 12.25C7.68944 12.25 8.37213 12.1142 9.00909 11.8504C9.64605 11.5865 10.2248 11.1998 10.7123 10.7123C11.1998 10.2248 11.5865 9.64605 11.8504 9.00909C12.1142 8.37213 12.25 7.68944 12.25 7C12.25 5.60761 11.6969 4.27226 10.7123 3.28769C9.72774 2.30312 8.39239 1.75 7 1.75C5.60761 1.75 4.27226 2.30312 3.28769 3.28769C2.30312 4.27226 1.75 5.60761 1.75 7Z" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M7 4.66699V7.00033" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M7 9.33301H7.00583" stroke-width="1.16667" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 967 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 6C4 7.06087 4.42143 8.07828 5.17157 8.82843C5.92172 9.57857 6.93913 10 8 10C9.06087 10 10.0783 9.57857 10.8284 8.82843C11.5786 8.07828 12 7.06087 12 6C12 4.93913 11.5786 3.92172 10.8284 3.17157C10.0783 2.42143 9.06087 2 8 2C6.93913 2 5.92172 2.42143 5.17157 3.17157C4.42143 3.92172 4 4.93913 4 6Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8 10L10.2667 13.9267L11.332 11.7713L13.7307 11.926L11.464 8" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M4.53473 8L2.26807 11.9267L4.66673 11.7713L5.73207 13.926L7.99873 10" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 830 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 8C2 8.78793 2.15519 9.56815 2.45672 10.2961C2.75825 11.0241 3.20021 11.6855 3.75736 12.2426C4.31451 12.7998 4.97595 13.2417 5.7039 13.5433C6.43185 13.8448 7.21207 14 8 14C8.78793 14 9.56815 13.8448 10.2961 13.5433C11.0241 13.2417 11.6855 12.7998 12.2426 12.2426C12.7998 11.6855 13.2417 11.0241 13.5433 10.2961C13.8448 9.56815 14 8.78793 14 8C14 6.4087 13.3679 4.88258 12.2426 3.75736C11.1174 2.63214 9.5913 2 8 2C6.4087 2 4.88258 2.63214 3.75736 3.75736C2.63214 4.88258 2 6.4087 2 8Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M2.3999 6H13.5999" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M2.3999 10H13.5999" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M7.66677 2C6.54367 3.79974 5.94824 5.87858 5.94824 8C5.94824 10.1214 6.54367 12.2003 7.66677 14" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8.33325 2C9.45636 3.79974 10.0518 5.87858 10.0518 8C10.0518 10.1214 9.45636 12.2003 8.33325 14" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.3334 7.33346C13.1704 6.16028 12.6261 5.07324 11.7845 4.23979C10.9429 3.40635 9.85059 2.87274 8.67586 2.72116C7.50114 2.56959 6.30916 2.80845 5.28355 3.40096C4.25794 3.99347 3.45558 4.90675 3.00008 6.00013M3.00008 6.00013H2.66675V3.33346M3.00008 6.00013H5.33341" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M2.66675 8.66699C2.82979 9.84018 3.37404 10.9272 4.21566 11.7607C5.05728 12.5941 6.14958 13.1277 7.3243 13.2793C8.49902 13.4309 9.691 13.192 10.7166 12.5995C11.7422 12.007 12.5446 11.0937 13.0001 10.0003M13.0001 10.0003H13.3334V12.667M13.0001 10.0003H10.6667" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 825 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.66675 9.33333L14.0001 2" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M14 2L9.66671 14C9.63746 14.0638 9.5905 14.1179 9.53141 14.1558C9.47233 14.1938 9.40359 14.2139 9.33338 14.2139C9.26317 14.2139 9.19444 14.1938 9.13535 14.1558C9.07626 14.1179 9.0293 14.0638 9.00005 14L6.66671 9.33333L2.00005 7C1.93622 6.97075 1.88213 6.92379 1.84421 6.8647C1.80629 6.80561 1.78613 6.73688 1.78613 6.66667C1.78613 6.59646 1.80629 6.52772 1.84421 6.46863C1.88213 6.40954 1.93622 6.36258 2.00005 6.33333L14 2Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 753 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.99997 2C9.55719 3.37772 11.5896 4.09504 13.6666 4C13.969 5.02868 14.0616 6.10764 13.9387 7.17279C13.8159 8.23793 13.4802 9.26751 12.9516 10.2004C12.423 11.1332 11.7122 11.9503 10.8616 12.603C10.011 13.2557 9.03783 13.7308 7.99997 14C6.96211 13.7308 5.98896 13.2557 5.13833 12.603C4.28771 11.9503 3.57698 11.1332 3.04837 10.2004C2.51976 9.26751 2.18406 8.23793 2.06122 7.17279C1.93838 6.10764 2.03091 5.02868 2.3333 4C4.41032 4.09504 6.44275 3.37772 7.99997 2Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 665 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 3.33333C4 2.97971 4.14048 2.64057 4.39052 2.39052C4.64057 2.14048 4.97971 2 5.33333 2H10.6667C11.0203 2 11.3594 2.14048 11.6095 2.39052C11.8595 2.64057 12 2.97971 12 3.33333V12.6667C12 13.0203 11.8595 13.3594 11.6095 13.6095C11.3594 13.8595 11.0203 14 10.6667 14H5.33333C4.97971 14 4.64057 13.8595 4.39052 13.6095C4.14048 13.3594 4 13.0203 4 12.6667V3.33333Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M7.33325 2.66699H8.66659" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8 11.333V11.3397" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 805 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.75 9.75C3.75 9.35218 3.90804 8.97064 4.18934 8.68934C4.47064 8.40804 4.85218 8.25 5.25 8.25H12.75C13.1478 8.25 13.5294 8.40804 13.8107 8.68934C14.092 8.97064 14.25 9.35218 14.25 9.75V14.25C14.25 14.6478 14.092 15.0294 13.8107 15.3107C13.5294 15.592 13.1478 15.75 12.75 15.75H5.25C4.85218 15.75 4.47064 15.592 4.18934 15.3107C3.90804 15.0294 3.75 14.6478 3.75 14.25V9.75Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M8.25 12C8.25 12.1989 8.32902 12.3897 8.46967 12.5303C8.61032 12.671 8.80109 12.75 9 12.75C9.19891 12.75 9.38968 12.671 9.53033 12.5303C9.67098 12.3897 9.75 12.1989 9.75 12C9.75 11.8011 9.67098 11.6103 9.53033 11.4697C9.38968 11.329 9.19891 11.25 9 11.25C8.80109 11.25 8.61032 11.329 8.46967 11.4697C8.32902 11.6103 8.25 11.8011 8.25 12Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M6 8.25V5.25C6 4.45435 6.31607 3.69129 6.87868 3.12868C7.44129 2.56607 8.20435 2.25 9 2.25C9.79565 2.25 10.5587 2.56607 11.1213 3.12868C11.6839 3.69129 12 4.45435 12 5.25V8.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.75 9.75C3.75 9.35218 3.90804 8.97064 4.18934 8.68934C4.47064 8.40804 4.85218 8.25 5.25 8.25H12.75C13.1478 8.25 13.5294 8.40804 13.8107 8.68934C14.092 8.97064 14.25 9.35218 14.25 9.75V14.25C14.25 14.6478 14.092 15.0294 13.8107 15.3107C13.5294 15.592 13.1478 15.75 12.75 15.75H5.25C4.85218 15.75 4.47064 15.592 4.18934 15.3107C3.90804 15.0294 3.75 14.6478 3.75 14.25V9.75Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M8.25 12C8.25 12.1989 8.32902 12.3897 8.46967 12.5303C8.61032 12.671 8.80109 12.75 9 12.75C9.19891 12.75 9.38968 12.671 9.53033 12.5303C9.67098 12.3897 9.75 12.1989 9.75 12C9.75 11.8011 9.67098 11.6103 9.53033 11.4697C9.38968 11.329 9.19891 11.25 9 11.25C8.80109 11.25 8.61032 11.329 8.46967 11.4697C8.32902 11.6103 8.25 11.8011 8.25 12Z" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
<path d="M6 8.25V5.25C6 4.45435 6.31607 3.69129 6.87868 3.12868C7.44129 2.56607 8.20435 2.25 9 2.25C9.79565 2.25 10.5587 2.56607 11.1213 3.12868C11.6839 3.69129 12 4.45435 12 5.25V8.25" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.5 6V4.5C10.5 4.10218 10.342 3.72064 10.0607 3.43934C9.77936 3.15804 9.39782 3 9 3H3.75C3.35218 3 2.97064 3.15804 2.68934 3.43934C2.40804 3.72064 2.25 4.10218 2.25 4.5V13.5C2.25 13.8978 2.40804 14.2794 2.68934 14.5607C2.97064 14.842 3.35218 15 3.75 15H9C9.39782 15 9.77936 14.842 10.0607 14.5607C10.342 14.2794 10.5 13.8978 10.5 13.5V12" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M6.75 9H15.75L13.5 6.75" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M13.5 11.25L15.75 9" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 772 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 4.66732C2 4.3137 2.14048 3.97456 2.39052 3.72451C2.64057 3.47446 2.97971 3.33398 3.33333 3.33398H12.6667C13.0203 3.33398 13.3594 3.47446 13.6095 3.72451C13.8595 3.97456 14 4.3137 14 4.66732V11.334C14 11.6876 13.8595 12.0267 13.6095 12.2768C13.3594 12.5268 13.0203 12.6673 12.6667 12.6673H3.33333C2.97971 12.6673 2.64057 12.5268 2.39052 12.2768C2.14048 12.0267 2 11.6876 2 11.334V4.66732Z" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
<path d="M2 4.66602L8 8.66602L14 4.66602" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round" stroke="#7E818B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 724 B |