Extract Vue frontend into separate container and add stp_wasm crate
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 4m58s
Move Vue app from nginx/vue/ to top-level vue/ with its own Dockerfile, update docker-compose configs and nginx proxy to serve from the new container, and add initial Rust WASM crate (stp_wasm). Also fix .gitignore to exclude Rust target/ directories. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4
.gitignore
vendored
@@ -9,8 +9,8 @@ gitea/config/app.ini
|
|||||||
gitea/data/*
|
gitea/data/*
|
||||||
gitea-runner/data/*
|
gitea-runner/data/*
|
||||||
|
|
||||||
# Will add in future (webpack)
|
# Rust build artifacts
|
||||||
nginx/vue/crates/
|
**/target/
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ docker compose up --build
|
|||||||
|
|
||||||
### Frontend only (hot reload)
|
### Frontend only (hot reload)
|
||||||
```
|
```
|
||||||
cd nginx/vue && npm run dev
|
cd vue && npm run dev
|
||||||
```
|
```
|
||||||
Vite dev server proxies `/api` to `localhost:8080`, `/gitea` to `localhost:3000`, `/radio` to `localhost:8000`.
|
Vite dev server proxies `/api` to `localhost:8080`, `/gitea` to `localhost:3000`, `/radio` to `localhost:8000`.
|
||||||
|
|
||||||
### Frontend build
|
### Frontend build
|
||||||
```
|
```
|
||||||
cd nginx/vue && npm run build
|
cd vue && npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Regenerate GraphQL (after editing schema files)
|
### Regenerate GraphQL (after editing schema files)
|
||||||
@@ -38,7 +38,7 @@ Dockerized multi-service personal website self-hosted on a Raspberry Pi.
|
|||||||
|
|
||||||
**Backend** (`backend/`): Go with Gin router. GraphQL API via gqlgen at `POST /api/graphql`. REST endpoints for auth, file uploads, Spotify OAuth, and WebSockets. GORM for PostgreSQL with auto-migrations (no separate migration files). JWT auth stored in HTTP-only cookies.
|
**Backend** (`backend/`): Go with Gin router. GraphQL API via gqlgen at `POST /api/graphql`. REST endpoints for auth, file uploads, Spotify OAuth, and WebSockets. GORM for PostgreSQL with auto-migrations (no separate migration files). JWT auth stored in HTTP-only cookies.
|
||||||
|
|
||||||
**Frontend** (`nginx/vue/`): Vue 3 SPA with Vite, Tailwind CSS v4, Pinia stores, Vue Router. Served as static files through Nginx.
|
**Frontend** (`vue/`): Vue 3 SPA with Vite, Tailwind CSS v4, Pinia stores, Vue Router. Built in a separate container; assets served through Nginx (production) or proxied to Vite dev server (dev mode).
|
||||||
|
|
||||||
**Nginx** (`nginx/`): Reverse proxy + SPA server. Config is templated (`nginx.conf.template`) and selected at runtime by `entrypoint.sh` based on `DEV_MODE` and certificate presence. Rate limiting on login (5/min), API (30/sec), uploads (5/min).
|
**Nginx** (`nginx/`): Reverse proxy + SPA server. Config is templated (`nginx.conf.template`) and selected at runtime by `entrypoint.sh` based on `DEV_MODE` and certificate presence. Rate limiting on login (5/min), API (30/sec), uploads (5/min).
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
services:
|
services:
|
||||||
|
vue:
|
||||||
|
command: ["npm", "run", "dev"]
|
||||||
|
volumes:
|
||||||
|
- ./vue:/app
|
||||||
|
- /app/node_modules
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=development
|
||||||
nginx:
|
nginx:
|
||||||
environment:
|
environment:
|
||||||
- DEV_MODE=true
|
- DEV_MODE=true
|
||||||
|
|||||||
@@ -5,8 +5,19 @@ networks:
|
|||||||
volumes:
|
volumes:
|
||||||
dbdata:
|
dbdata:
|
||||||
uploads:
|
uploads:
|
||||||
|
vue_dist:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
vue:
|
||||||
|
build:
|
||||||
|
context: ./vue
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: vue
|
||||||
|
volumes:
|
||||||
|
- vue_dist:/output
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
|
||||||
nginx:
|
nginx:
|
||||||
build:
|
build:
|
||||||
context: ./nginx
|
context: ./nginx
|
||||||
@@ -15,6 +26,7 @@ services:
|
|||||||
env_file: ./.env
|
env_file: ./.env
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
|
- vue
|
||||||
- backend
|
- backend
|
||||||
- icecast2
|
- icecast2
|
||||||
- gitea
|
- gitea
|
||||||
@@ -27,6 +39,7 @@ services:
|
|||||||
- ./certbot/conf:/etc/letsencrypt
|
- ./certbot/conf:/etc/letsencrypt
|
||||||
- ./certbot/www:/var/www/certbot
|
- ./certbot/www:/var/www/certbot
|
||||||
- uploads:/uploads
|
- uploads:/uploads
|
||||||
|
- vue_dist:/etc/nginx/html
|
||||||
|
|
||||||
certbot:
|
certbot:
|
||||||
image: certbot/certbot
|
image: certbot/certbot
|
||||||
|
|||||||
@@ -1,5 +1,2 @@
|
|||||||
vue/node_modules
|
|
||||||
vue/.vite
|
|
||||||
vue/dist
|
|
||||||
**/.git
|
**/.git
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
# Stage 1: Build Vue app
|
|
||||||
FROM node:22-slim AS build
|
|
||||||
RUN apt-get update && apt-get install -y make git && rm -rf /var/lib/apt/lists/*
|
|
||||||
WORKDIR /app
|
|
||||||
COPY vue/package.json vue/package-lock.json ./
|
|
||||||
RUN npm ci
|
|
||||||
COPY vue/ ./
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# Stage 2: Serve with nginx
|
|
||||||
FROM nginx:latest
|
FROM nginx:latest
|
||||||
RUN rm -rf /etc/nginx/html/* && \
|
RUN rm -rf /etc/nginx/html/* && \
|
||||||
apt-get update && apt-get install -y gettext-base && \
|
apt-get update && apt-get install -y gettext-base && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
COPY --from=build /app/dist /etc/nginx/html/
|
|
||||||
COPY nginx.conf.template /etc/nginx/nginx.conf.template
|
COPY nginx.conf.template /etc/nginx/nginx.conf.template
|
||||||
COPY nginx_setup.conf.template /etc/nginx/nginx_setup.conf.template
|
COPY nginx_setup.conf.template /etc/nginx/nginx_setup.conf.template
|
||||||
COPY nginx_dev.conf.template /etc/nginx/nginx_dev.conf.template
|
COPY nginx_dev.conf.template /etc/nginx/nginx_dev.conf.template
|
||||||
COPY robots.txt /etc/nginx/html/robots.txt
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
|||||||
@@ -20,5 +20,20 @@ fi
|
|||||||
# Ensure upload directory is traversable by nginx worker
|
# Ensure upload directory is traversable by nginx worker
|
||||||
chmod 755 /uploads 2>/dev/null || true
|
chmod 755 /uploads 2>/dev/null || true
|
||||||
|
|
||||||
|
# Wait for Vue assets in production mode
|
||||||
|
if [ "$DEV_MODE" != "true" ]; then
|
||||||
|
echo "Waiting for Vue assets..."
|
||||||
|
elapsed=0
|
||||||
|
while [ ! -f /etc/nginx/html/index.html ] && [ $elapsed -lt 120 ]; do
|
||||||
|
sleep 1
|
||||||
|
elapsed=$((elapsed + 1))
|
||||||
|
done
|
||||||
|
if [ ! -f /etc/nginx/html/index.html ]; then
|
||||||
|
echo "WARNING: Vue assets not found after 120s, starting nginx anyway"
|
||||||
|
else
|
||||||
|
echo "Vue assets ready."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Start nginx
|
# Start nginx
|
||||||
nginx -g 'daemon off;'
|
nginx -g 'daemon off;'
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ http {
|
|||||||
listen 80;
|
listen 80;
|
||||||
server_name $DOMAIN www.$DOMAIN;
|
server_name $DOMAIN www.$DOMAIN;
|
||||||
|
|
||||||
root /etc/nginx/html;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location /uploads/ {
|
location /uploads/ {
|
||||||
alias /uploads/;
|
alias /uploads/;
|
||||||
add_header X-Content-Type-Options nosniff always;
|
add_header X-Content-Type-Options nosniff always;
|
||||||
@@ -39,17 +36,11 @@ http {
|
|||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.html;
|
proxy_pass http://vue:5173;
|
||||||
}
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
location = /robots.txt {
|
proxy_set_header Connection "upgrade";
|
||||||
allow all;
|
proxy_set_header Host $host;
|
||||||
log_not_found off;
|
|
||||||
access_log off;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /img/stamps/mine.gif {
|
|
||||||
add_header Access-Control-Allow-Origin *;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
location $BACKEND_ENDPOINT {
|
location $BACKEND_ENDPOINT {
|
||||||
|
|||||||
5
vue/.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules
|
||||||
|
.vite
|
||||||
|
dist
|
||||||
|
**/.git
|
||||||
|
**/.DS_Store
|
||||||
7
vue/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM node:22-slim
|
||||||
|
RUN apt-get update && apt-get install -y make git && rm -rf /var/lib/apt/lists/*
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY . .
|
||||||
|
CMD ["sh", "-c", "npm run build -- --outDir /output --emptyOutDir"]
|
||||||
136
vue/crates/stp_wasm/Cargo.lock
generated
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.19.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.85"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.106"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stp_wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.116"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.108"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.108"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.108"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.108"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.85"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
15
vue/crates/stp_wasm/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "stp_wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
js-sys = "0.3.85"
|
||||||
|
wasm-bindgen = "0.2.108"
|
||||||
|
web-sys = { version = "0.3.85", features = [
|
||||||
|
"console",
|
||||||
|
"Document",
|
||||||
|
"Element",
|
||||||
|
"Window",
|
||||||
|
"Animation",
|
||||||
|
] }
|
||||||
6
vue/crates/stp_wasm/src/lib.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub struct BadApplePlayer {
|
||||||
|
is_playing: bool,
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 756 KiB After Width: | Height: | Size: 756 KiB |
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 1021 KiB After Width: | Height: | Size: 1021 KiB |
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 3.3 MiB |
|
Before Width: | Height: | Size: 948 B After Width: | Height: | Size: 948 B |
|
Before Width: | Height: | Size: 970 B After Width: | Height: | Size: 970 B |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 477 B |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 4.7 MiB After Width: | Height: | Size: 4.7 MiB |
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 246 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 573 KiB After Width: | Height: | Size: 573 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 650 KiB After Width: | Height: | Size: 650 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 536 KiB After Width: | Height: | Size: 536 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |