monorepo packages (#344)
6
.github/workflows/fastgpt-image.yml
vendored
@ -3,7 +3,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- 'client/**'
|
- 'projects/app/**'
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
tags:
|
tags:
|
||||||
@ -49,8 +49,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||||
run: |
|
run: |
|
||||||
cd client && \
|
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
|
--build-arg name=app \
|
||||||
--platform linux/amd64,linux/arm64 \
|
--platform linux/amd64,linux/arm64 \
|
||||||
--label "org.opencontainers.image.source= https://github.com/ ${{ github.repository_owner }}/FastGPT" \
|
--label "org.opencontainers.image.source= https://github.com/ ${{ github.repository_owner }}/FastGPT" \
|
||||||
--label "org.opencontainers.image.description=fastgpt image" \
|
--label "org.opencontainers.image.description=fastgpt image" \
|
||||||
@ -64,6 +64,7 @@ jobs:
|
|||||||
push-to-docker-hub:
|
push-to-docker-hub:
|
||||||
needs: build-fastgpt-images
|
needs: build-fastgpt-images
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
if: github.repository == 'labring/FastGPT'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -87,6 +88,7 @@ jobs:
|
|||||||
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}:${{env.IMAGE_TAG}}
|
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}:${{env.IMAGE_TAG}}
|
||||||
push-to-ali-hub:
|
push-to-ali-hub:
|
||||||
needs: build-fastgpt-images
|
needs: build-fastgpt-images
|
||||||
|
if: github.repository == 'labring/FastGPT'
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
|
|||||||
4
.vscode/settings.json
vendored
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"editor.mouseWheelZoom": true,
|
"editor.mouseWheelZoom": true,
|
||||||
"typescript.tsdk": "client/node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"prettier.prettierPath": "./node_modules/prettier",
|
"prettier.prettierPath": "./node_modules/prettier",
|
||||||
"i18n-ally.localesPaths": [
|
"i18n-ally.localesPaths": [
|
||||||
"client/public/locales"
|
"projects/app/public/locales"
|
||||||
],
|
],
|
||||||
"i18n-ally.enabledParsers": ["json"],
|
"i18n-ally.enabledParsers": ["json"],
|
||||||
"i18n-ally.keystyle": "nested",
|
"i18n-ally.keystyle": "nested",
|
||||||
|
|||||||
71
Dockerfile
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# Install dependencies only when needed
|
||||||
|
FROM node:current-alpine AS deps
|
||||||
|
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||||
|
RUN apk add --no-cache libc6-compat && npm install -g pnpm
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ARG name
|
||||||
|
|
||||||
|
# copy packages and one project
|
||||||
|
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
|
COPY ./packages ./packages
|
||||||
|
COPY ./projects/$name/package.json ./projects/$name/package.json
|
||||||
|
COPY ./projects/$name/pnpm-lock.yaml ./projects/$name/pnpm-lock.yaml
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
[ -f pnpm-lock.yaml ] && pnpm install || \
|
||||||
|
(echo "Lockfile not found." && exit 1)
|
||||||
|
|
||||||
|
RUN pnpm prune
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM node:current-alpine AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ARG name
|
||||||
|
|
||||||
|
# copy common node_modules and one project node_modules
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY ./projects/$name ./projects/$name
|
||||||
|
COPY --from=deps /app/projects/$name/node_modules ./projects/$name/node_modules
|
||||||
|
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||||
|
COPY ./packages ./packages
|
||||||
|
|
||||||
|
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
RUN npm install -g pnpm
|
||||||
|
RUN pnpm --filter=$name run build
|
||||||
|
|
||||||
|
FROM node:current-alpine AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ARG name
|
||||||
|
|
||||||
|
# create user and use it
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
RUN sed -i 's/https/http/' /etc/apk/repositories
|
||||||
|
RUN apk add curl \
|
||||||
|
&& apk add ca-certificates \
|
||||||
|
&& update-ca-certificates
|
||||||
|
|
||||||
|
# copy running files
|
||||||
|
COPY --from=builder /app/projects/$name/public ./projects/$name/public
|
||||||
|
COPY --from=builder /app/projects/$name/next.config.js ./projects/$name/next.config.js
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/static ./projects/$name/.next/static
|
||||||
|
# copy package.json to version file
|
||||||
|
COPY --from=builder /app/projects/$name/package.json ./package.json
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
ENV PORT=3000
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
ENV serverPath=./projects/$name/server.js
|
||||||
|
|
||||||
|
ENTRYPOINT ["sh","-c","node ${serverPath}"]
|
||||||
@ -1,65 +0,0 @@
|
|||||||
# Install dependencies only when needed
|
|
||||||
FROM node:current-alpine AS deps
|
|
||||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
|
||||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Install dependencies based on the preferred package manager
|
|
||||||
COPY package.json ./
|
|
||||||
COPY pnpm-lock.yaml* ./
|
|
||||||
RUN \
|
|
||||||
[ -f pnpm-lock.yaml ] && pnpm fetch || \
|
|
||||||
(echo "Lockfile not found." && exit 1)
|
|
||||||
|
|
||||||
# Rebuild the source code only when needed
|
|
||||||
FROM node:current-alpine AS builder
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
|
||||||
COPY pnpm-lock.yaml* ./
|
|
||||||
COPY package.json ./
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Next.js collects completely anonymous telemetry data about general usage.
|
|
||||||
# Learn more here: https://nextjs.org/telemetry
|
|
||||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
|
||||||
|
|
||||||
RUN npm install -g pnpm
|
|
||||||
RUN \
|
|
||||||
[ -f pnpm-lock.yaml ] && (pnpm --offline install && pnpm run build) || \
|
|
||||||
(echo "Lockfile not found." && exit 1)
|
|
||||||
|
|
||||||
# Production image, copy all the files and run next
|
|
||||||
FROM node:current-alpine AS runner
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENV NODE_ENV production
|
|
||||||
# Uncomment the following line in case you want to disable telemetry during runtime.
|
|
||||||
ENV NEXT_TELEMETRY_DISABLED 1
|
|
||||||
|
|
||||||
RUN addgroup --system --gid 1001 nodejs
|
|
||||||
RUN adduser --system --uid 1001 nextjs
|
|
||||||
|
|
||||||
RUN sed -i 's/https/http/' /etc/apk/repositories
|
|
||||||
RUN apk add curl \
|
|
||||||
&& apk add ca-certificates \
|
|
||||||
&& update-ca-certificates
|
|
||||||
|
|
||||||
# You only need to copy next.config.js if you are NOT using the default configuration
|
|
||||||
# COPY --from=builder /app/next.config.js ./
|
|
||||||
COPY --from=builder /app/public ./public
|
|
||||||
COPY --from=builder /app/package.json ./package.json
|
|
||||||
# COPY --from=builder /app/.env* .
|
|
||||||
|
|
||||||
# Automatically leverage output traces to reduce image size
|
|
||||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
||||||
|
|
||||||
USER nextjs
|
|
||||||
|
|
||||||
ENV PORT=3000
|
|
||||||
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
|
||||||
5
client/next-env.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
/// <reference types="next" />
|
|
||||||
/// <reference types="next/image-types/global" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
|
||||||
@ -7,7 +7,7 @@ toc: true
|
|||||||
weight: 520
|
weight: 520
|
||||||
---
|
---
|
||||||
|
|
||||||
由于环境变量不利于配置复杂的内容,新版 FastGPT 采用了 ConfigMap 的形式挂载配置文件,你可以在 `client/data/config.json` 看到默认的配置文件。可以参考 [docker-compose 快速部署](/docs/installation/docker/) 来挂载配置文件。
|
由于环境变量不利于配置复杂的内容,新版 FastGPT 采用了 ConfigMap 的形式挂载配置文件,你可以在 `projects/app/data/config.json` 看到默认的配置文件。可以参考 [docker-compose 快速部署](/docs/installation/docker/) 来挂载配置文件。
|
||||||
|
|
||||||
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
|
**开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,8 @@ weight: 510
|
|||||||
git clone git@github.com:<github_username>/FastGPT.git
|
git clone git@github.com:<github_username>/FastGPT.git
|
||||||
```
|
```
|
||||||
|
|
||||||
client 目录下为 FastGPT 核心代码。NextJS 框架前后端放在一起,API 服务位于 `src/pages/api` 目录内。
|
projects 目录下为 FastGPT 应用代码。NextJS 框架前后端放在一起,API 服务位于 `src/pages/api` 目录内。
|
||||||
|
packages 目录为相关的共用包。
|
||||||
|
|
||||||
### 安装数据库
|
### 安装数据库
|
||||||
|
|
||||||
|
|||||||
@ -92,7 +92,7 @@ CHAT_API_KEY=sk-xxxxxx
|
|||||||
|
|
||||||
### 2. 修改 FastGPT 配置文件
|
### 2. 修改 FastGPT 配置文件
|
||||||
|
|
||||||
可以在 `/client/src/data/config.json` 里找到配置文件(本地开发需要复制成 config.local.json),配置文件中有一项是对话模型配置:
|
可以在 `/projects/app/src/data/config.json` 里找到配置文件(本地开发需要复制成 config.local.json),配置文件中有一项是对话模型配置:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"ChatModels": [
|
"ChatModels": [
|
||||||
|
|||||||
1
packages/common/bill/constants.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const PRICE_SCALE = 100000;
|
||||||
9
packages/common/bill/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* bill common */
|
||||||
|
import { PRICE_SCALE } from './constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dataset price / PRICE_SCALE = real price
|
||||||
|
*/
|
||||||
|
export const formatPrice = (val = 0, multiple = 1) => {
|
||||||
|
return Number(((val / PRICE_SCALE) * multiple).toFixed(10));
|
||||||
|
};
|
||||||
4
packages/common/package.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "@fastgpt/common",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
21
packages/common/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2015",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"baseUrl": "."
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
4
packages/core/package.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "@fastgpt/core",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
21
packages/core/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2015",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"baseUrl": "."
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
4
packages/support/package.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "@fastgpt/support",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
21
packages/support/tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2015",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"baseUrl": "."
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.d.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
11950
pnpm-lock.yaml
generated
3
pnpm-workspace.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
packages:
|
||||||
|
- 'packages/*'
|
||||||
|
- 'projects/*'
|
||||||
@ -1,12 +1,13 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const { i18n } = require('./next-i18next.config');
|
const { i18n } = require('./next-i18next.config');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
i18n,
|
i18n,
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
reactStrictMode: false,
|
reactStrictMode: false,
|
||||||
compress: true,
|
compress: true,
|
||||||
|
transpilePackages: ['@fastgpt/*'],
|
||||||
webpack(config, { isServer }) {
|
webpack(config, { isServer }) {
|
||||||
if (!isServer) {
|
if (!isServer) {
|
||||||
config.resolve = {
|
config.resolve = {
|
||||||
@ -30,6 +31,9 @@ const nextConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
},
|
||||||
|
experimental: {
|
||||||
|
outputFileTracingRoot: path.join(__dirname, '../../')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "fastgpt",
|
"name": "app",
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -15,7 +15,6 @@
|
|||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
"@mozilla/readability": "^0.4.4",
|
"@mozilla/readability": "^0.4.4",
|
||||||
"@next/font": "13.1.6",
|
|
||||||
"@tanstack/react-query": "^4.24.10",
|
"@tanstack/react-query": "^4.24.10",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
@ -42,8 +41,8 @@
|
|||||||
"mongoose": "^6.10.0",
|
"mongoose": "^6.10.0",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
"nanoid": "^4.0.1",
|
"nanoid": "^4.0.1",
|
||||||
"next": "13.1.6",
|
"next": "13.5.2",
|
||||||
"next-i18next": "^13.3.0",
|
"next-i18next": "^14.0.3",
|
||||||
"nextjs-cors": "^2.1.2",
|
"nextjs-cors": "^2.1.2",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"openai": "^3.3.0",
|
"openai": "^3.3.0",
|
||||||
@ -68,7 +67,10 @@
|
|||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
"winston": "^3.10.0",
|
"winston": "^3.10.0",
|
||||||
"winston-mongodb": "^5.1.1",
|
"winston-mongodb": "^5.1.1",
|
||||||
"zustand": "^4.3.5"
|
"zustand": "^4.3.5",
|
||||||
|
"@fastgpt/support": "workspace:*",
|
||||||
|
"@fastgpt/common": "workspace:*",
|
||||||
|
"@fastgpt/core": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@svgr/webpack": "^6.5.1",
|
"@svgr/webpack": "^6.5.1",
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1006 B After Width: | Height: | Size: 1006 B |
|
Before Width: | Height: | Size: 428 KiB After Width: | Height: | Size: 428 KiB |
|
Before Width: | Height: | Size: 596 KiB After Width: | Height: | Size: 596 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.6 MiB |
|
Before Width: | Height: | Size: 455 KiB After Width: | Height: | Size: 455 KiB |
|
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 776 B After Width: | Height: | Size: 776 B |
|
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 572 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |