Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
06a7cbb0f1 | ||
|
24e0ff0129 | ||
|
45fafc3507 | ||
|
9e0c92d703 | ||
|
03f8cbfe7b | ||
|
120ea02fde | ||
|
cc16271683 | ||
|
78a47e3a74 | ||
|
b4c2ebc0bb | ||
|
55112fac88 | ||
|
4f2bf7a64e | ||
|
dfcbea56b8 | ||
|
f87c2c9d50 | ||
|
4cc2dd4b54 | ||
|
f1a0de2090 | ||
|
1918af3f0e | ||
|
f958129d09 | ||
|
45735e7908 | ||
|
4d9a5fdaad |
15
.devcontainer/Dockerfile
Normal file
15
.devcontainer/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
FROM mcr.microsoft.com/devcontainers/python:1-3.11-bullseye
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
|
# [Optional] If your requirements rarely change, uncomment this section to add them to the image.
|
||||||
|
# COPY requirements.txt /tmp/pip-tmp/
|
||||||
|
# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
|
||||||
|
# && rm -rf /tmp/pip-tmp
|
||||||
|
|
||||||
|
# [Optional] Uncomment this section to install additional OS packages.
|
||||||
|
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
|
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||||
|
|
||||||
|
|
||||||
|
|
29
.devcontainer/devcontainer.json
Normal file
29
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/postgres
|
||||||
|
{
|
||||||
|
"name": "Python 3 & PostgreSQL",
|
||||||
|
"dockerComposeFile": "docker-compose.yml",
|
||||||
|
"service": "app",
|
||||||
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
|
||||||
|
"ghcr.io/devcontainers/features/node:1": {},
|
||||||
|
"ghcr.io/devcontainers-contrib/features/poetry:2": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// This can be used to network with other containers or the host.
|
||||||
|
// "forwardPorts": [5000, 5432],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "pip install --user -r requirements.txt",
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
35
.devcontainer/docker-compose.yml
Normal file
35
.devcontainer/docker-compose.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: ..
|
||||||
|
dockerfile: .devcontainer/Dockerfile
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- ../..:/workspaces:cached
|
||||||
|
|
||||||
|
# Overrides default command so things don't shut down after the process ends.
|
||||||
|
command: sleep infinity
|
||||||
|
|
||||||
|
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
|
||||||
|
network_mode: service:db
|
||||||
|
|
||||||
|
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
|
||||||
|
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_DB: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|
||||||
|
# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
|
||||||
|
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres-data:
|
38
.github/workflows/build-docker-edge.yml
vendored
38
.github/workflows/build-docker-edge.yml
vendored
@ -9,40 +9,50 @@ jobs:
|
|||||||
publish_to_docker_hub:
|
publish_to_docker_hub:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Create Docker Metadata
|
||||||
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
milesmcc/shynet
|
||||||
|
ghcr.io/milesmcc/shynet
|
||||||
|
tags:
|
||||||
|
type=edge
|
||||||
|
|
||||||
- name: Set swap space
|
- name: Set swap space
|
||||||
uses: pierotofy/set-swap-space@master
|
uses: pierotofy/set-swap-space@master
|
||||||
with:
|
with:
|
||||||
swap-size-gb: 5
|
swap-size-gb: 5
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Prepare tags
|
|
||||||
id: prep
|
|
||||||
run: |
|
|
||||||
DOCKER_IMAGE=milesmcc/shynet
|
|
||||||
TAGS="${DOCKER_IMAGE}:edge"
|
|
||||||
echo ::set-output name=tags::${TAGS}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
uses: docker/setup-buildx-action@v3
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push advanced image
|
- name: Build and push advanced image
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
38
.github/workflows/build-docker-manual.yml
vendored
38
.github/workflows/build-docker-manual.yml
vendored
@ -9,40 +9,50 @@ jobs:
|
|||||||
publish_to_docker_hub:
|
publish_to_docker_hub:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Create Docker Metadata
|
||||||
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
milesmcc/shynet
|
||||||
|
ghcr.io/milesmcc/shynet
|
||||||
|
tags:
|
||||||
|
type=raw,value=${{ github.event.inputs.tag }}
|
||||||
|
|
||||||
- name: Set swap space
|
- name: Set swap space
|
||||||
uses: pierotofy/set-swap-space@master
|
uses: pierotofy/set-swap-space@master
|
||||||
with:
|
with:
|
||||||
swap-size-gb: 5
|
swap-size-gb: 5
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Prepare tags
|
|
||||||
id: prep
|
|
||||||
run: |
|
|
||||||
DOCKER_IMAGE=milesmcc/shynet
|
|
||||||
TAGS="${DOCKER_IMAGE}:${{ github.event.inputs.tag }}"
|
|
||||||
echo ::set-output name=tags::${TAGS}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
uses: docker/setup-buildx-action@v3
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push advanced image
|
- name: Build and push advanced image
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
||||||
|
41
.github/workflows/build-docker-release.yml
vendored
41
.github/workflows/build-docker-release.yml
vendored
@ -9,41 +9,52 @@ jobs:
|
|||||||
publish_to_docker_hub:
|
publish_to_docker_hub:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
# https://github.com/docker/metadata-action/tree/v4/#typeref
|
||||||
|
- name: Create Docker Metadata
|
||||||
|
id: metadata
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
milesmcc/shynet
|
||||||
|
ghcr.io/milesmcc/shynet
|
||||||
|
tags:
|
||||||
|
type=raw,value=latest
|
||||||
|
type=ref,event=tag
|
||||||
|
|
||||||
- name: Set swap space
|
- name: Set swap space
|
||||||
uses: pierotofy/set-swap-space@master
|
uses: pierotofy/set-swap-space@master
|
||||||
with:
|
with:
|
||||||
swap-size-gb: 5
|
swap-size-gb: 5
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Prepare tags
|
|
||||||
id: prep
|
|
||||||
run: |
|
|
||||||
DOCKER_IMAGE=milesmcc/shynet
|
|
||||||
VERSION=${GITHUB_REF#refs/tags/}
|
|
||||||
TAGS="${DOCKER_IMAGE}:${VERSION},${DOCKER_IMAGE}:latest"
|
|
||||||
echo ::set-output name=tags::${TAGS}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
uses: docker/setup-buildx-action@v3
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
|
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and push advanced image
|
- name: Build and push advanced image
|
||||||
id: docker_build
|
id: docker_build
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.prep.outputs.tags }}
|
tags: ${{ steps.metadata.outputs.tags }}
|
||||||
|
labels: ${{ steps.metadata.outputs.labels }}
|
5
.github/workflows/run-tests.yml
vendored
5
.github/workflows/run-tests.yml
vendored
@ -27,10 +27,11 @@ jobs:
|
|||||||
- name: Run image
|
- name: Run image
|
||||||
uses: abatilo/actions-poetry@v2.0.0
|
uses: abatilo/actions-poetry@v2.0.0
|
||||||
with:
|
with:
|
||||||
poetry-version: 1.1.6
|
poetry-version: 1.2.2
|
||||||
|
- name: Preinstall dependencies (temporary)
|
||||||
|
run: poetry run pip install "Cython<3.0" "pyyaml==5.4.1" --no-build-isolation
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: poetry install
|
run: poetry install
|
||||||
|
|
||||||
- name: Django Testing project
|
- name: Django Testing project
|
||||||
run: |
|
run: |
|
||||||
cp TEMPLATE.env .env
|
cp TEMPLATE.env .env
|
||||||
|
32
Dockerfile
32
Dockerfile
@ -7,36 +7,34 @@ WORKDIR /usr/src/shynet
|
|||||||
ARG GF_UID="500"
|
ARG GF_UID="500"
|
||||||
ARG GF_GID="500"
|
ARG GF_GID="500"
|
||||||
RUN apk update && \
|
RUN apk update && \
|
||||||
apk add gettext curl bash npm libffi-dev rust cargo
|
apk add --no-cache gettext bash npm postgresql-libs && \
|
||||||
|
test "$(arch)" != "x86_64" && apk add libffi-dev rust cargo || echo "amd64 build, skipping Rust installation"
|
||||||
# libffi-dev and rust are used for the cryptography package,
|
# libffi-dev and rust are used for the cryptography package,
|
||||||
# which we indirectly rely on. Necessary for aarch64 support.
|
# which we indirectly rely on. Necessary for aarch64 support.
|
||||||
|
|
||||||
# Collect GeoIP Database
|
# Collect GeoIP Database
|
||||||
RUN curl -m 180 "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=kKG1ebhL3iWVd0iv&suffix=tar.gz" | tar -xvz -C /tmp && \
|
RUN apk add --no-cache curl && \
|
||||||
curl -m 180 "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=kKG1ebhL3iWVd0iv&suffix=tar.gz" | tar -xvz -C /tmp && \
|
curl -m 180 "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-ASN&license_key=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&suffix=tar.gz" | tar -xvz -C /tmp && \
|
||||||
|
curl -m 180 "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key=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&suffix=tar.gz" | tar -xvz -C /tmp && \
|
||||||
mv /tmp/GeoLite2*/*.mmdb /etc && \
|
mv /tmp/GeoLite2*/*.mmdb /etc && \
|
||||||
apk del curl
|
apk --purge del curl
|
||||||
|
|
||||||
# Move dependency files
|
# Move dependency files
|
||||||
COPY poetry.lock pyproject.toml ./
|
COPY poetry.lock pyproject.toml ./
|
||||||
COPY package.json package-lock.json ../
|
COPY package.json package-lock.json ../
|
||||||
# Django expects node_modules to be in its parent directory.
|
# Django expects node_modules to be in its parent directory.
|
||||||
|
|
||||||
# Install more dependencies
|
# Install more dependencies and cleanup build dependencies afterwards
|
||||||
RUN apk add --no-cache postgresql-libs && \
|
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev libressl-dev libffi-dev && \
|
||||||
apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev && \
|
|
||||||
npm i -P --prefix .. && \
|
npm i -P --prefix .. && \
|
||||||
pip install poetry==1.2.2
|
pip install poetry==1.2.2 && \
|
||||||
|
poetry config virtualenvs.create false && \
|
||||||
|
poetry run pip install "Cython<3.0" "pyyaml==5.4.1" --no-build-isolation && \
|
||||||
|
poetry install --no-dev --no-interaction --no-ansi && \
|
||||||
|
apk --purge del .build-deps
|
||||||
|
|
||||||
# Install Python dependencies
|
# Setup user group
|
||||||
RUN poetry config virtualenvs.create false && \
|
RUN addgroup --system -g $GF_GID appgroup && \
|
||||||
poetry install --no-dev --no-interaction --no-ansi
|
|
||||||
|
|
||||||
# Cleanup dependencies & setup user group
|
|
||||||
RUN apk --purge del .build-deps && \
|
|
||||||
rm -rf /var/lib/apt/lists/* && \
|
|
||||||
rm /var/cache/apk/* && \
|
|
||||||
addgroup --system -g $GF_GID appgroup && \
|
|
||||||
adduser appuser --system --uid $GF_UID -G appgroup && \
|
adduser appuser --system --uid $GF_UID -G appgroup && \
|
||||||
mkdir -p /var/local/shynet/db/ && \
|
mkdir -p /var/local/shynet/db/ && \
|
||||||
chown -R appuser:appgroup /var/local/shynet
|
chown -R appuser:appgroup /var/local/shynet
|
||||||
|
2
GUIDE.md
2
GUIDE.md
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
**If you install Shynet, you should strongly consider enabling notifications when new versions are released.** You can do this under the "Watch" tab on GitHub (above). This will ensure that you are notified when new versions are available, some of which may be security updates. (Shynet will never automatically update itself.)
|
**If you install Shynet, you should strongly consider enabling notifications when new versions are released.** You can do this under the "Watch" tab on GitHub (above). This will ensure that you are notified when new versions are available, some of which may be security updates. (Shynet will never automatically update itself.)
|
||||||
|
|
||||||
> **When you do update, read the release notes!** These will tell you if you need to make changes to your deployment. (E.g., Shynet 0.13.0 requires additional configuration.)
|
> **When you do update, read the release notes!** These will tell you if you need to make changes to your deployment. (E.g., Shynet 0.13.1 requires additional configuration.)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -673,9 +673,9 @@
|
|||||||
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
|
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
|
||||||
},
|
},
|
||||||
"node_modules/word-wrap": {
|
"node_modules/word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@ -1243,9 +1243,9 @@
|
|||||||
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
|
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
|
||||||
},
|
},
|
||||||
"word-wrap": {
|
"word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
|
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA=="
|
||||||
},
|
},
|
||||||
"wordwrap": {
|
"wordwrap": {
|
||||||
"version": "0.0.3",
|
"version": "0.0.3",
|
||||||
|
221
poetry.lock
generated
221
poetry.lock
generated
@ -1,91 +1,106 @@
|
|||||||
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp"
|
name = "aiohttp"
|
||||||
version = "3.8.1"
|
version = "3.8.5"
|
||||||
description = "Async http client/server framework (asyncio)"
|
description = "Async http client/server framework (asyncio)"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
|
{file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a94159871304770da4dd371f4291b20cac04e8c94f11bdea1c3478e557fbe0d8"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
|
{file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13bf85afc99ce6f9ee3567b04501f18f9f8dbbb2ea11ed1a2e079670403a7c84"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
|
{file = "aiohttp-3.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ce2ac5708501afc4847221a521f7e4b245abf5178cf5ddae9d5b3856ddb2f3a"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
|
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96943e5dcc37a6529d18766597c491798b7eb7a61d48878611298afc1fca946c"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
|
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ad5c3c4590bb3cc28b4382f031f3783f25ec223557124c68754a2231d989e2b"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
|
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c413c633d0512df4dc7fd2373ec06cc6a815b7b6d6c2f208ada7e9e93a5061d"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
|
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df72ac063b97837a80d80dec8d54c241af059cc9bb42c4de68bd5b61ceb37caa"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
|
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c48c5c0271149cfe467c0ff8eb941279fd6e3f65c9a388c984e0e6cf57538e14"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
|
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:368a42363c4d70ab52c2c6420a57f190ed3dfaca6a1b19afda8165ee16416a82"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
|
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7607ec3ce4993464368505888af5beb446845a014bc676d349efec0e05085905"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
|
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0d21c684808288a98914e5aaf2a7c6a3179d4df11d249799c32d1808e79503b5"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
|
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:312fcfbacc7880a8da0ae8b6abc6cc7d752e9caa0051a53d217a650b25e9a691"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
|
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad093e823df03bb3fd37e7dec9d4670c34f9e24aeace76808fc20a507cace825"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
|
{file = "aiohttp-3.8.5-cp310-cp310-win32.whl", hash = "sha256:33279701c04351a2914e1100b62b2a7fdb9a25995c4a104259f9a5ead7ed4802"},
|
||||||
{file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
|
{file = "aiohttp-3.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:6e4a280e4b975a2e7745573e3fc9c9ba0d1194a3738ce1cbaa80626cc9b4f4df"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
|
{file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae871a964e1987a943d83d6709d20ec6103ca1eaf52f7e0d36ee1b5bebb8b9b9"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
|
{file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:461908b2578955045efde733719d62f2b649c404189a09a632d245b445c9c975"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
|
{file = "aiohttp-3.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72a860c215e26192379f57cae5ab12b168b75db8271f111019509a1196dfc780"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
|
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc14be025665dba6202b6a71cfcdb53210cc498e50068bc088076624471f8bb9"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
|
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af740fc2711ad85f1a5c034a435782fbd5b5f8314c9a3ef071424a8158d7f6b"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
|
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:841cd8233cbd2111a0ef0a522ce016357c5e3aff8a8ce92bcfa14cef890d698f"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
|
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed1c46fb119f1b59304b5ec89f834f07124cd23ae5b74288e364477641060ff"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
|
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84f8ae3e09a34f35c18fa57f015cc394bd1389bce02503fb30c394d04ee6b938"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
|
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62360cb771707cb70a6fd114b9871d20d7dd2163a0feafe43fd115cfe4fe845e"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
|
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23fb25a9f0a1ca1f24c0a371523546366bb642397c94ab45ad3aedf2941cec6a"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
|
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0ba0d15164eae3d878260d4c4df859bbdc6466e9e6689c344a13334f988bb53"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
|
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5d20003b635fc6ae3f96d7260281dfaf1894fc3aa24d1888a9b2628e97c241e5"},
|
||||||
{file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
|
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0175d745d9e85c40dcc51c8f88c74bfbaef9e7afeeeb9d03c37977270303064c"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
|
{file = "aiohttp-3.8.5-cp311-cp311-win32.whl", hash = "sha256:2e1b1e51b0774408f091d268648e3d57f7260c1682e7d3a63cb00d22d71bb945"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
|
{file = "aiohttp-3.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:043d2299f6dfdc92f0ac5e995dfc56668e1587cea7f9aa9d8a78a1b6554e5755"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cae533195e8122584ec87531d6df000ad07737eaa3c81209e85c928854d2195c"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f21e83f355643c345177a5d1d8079f9f28b5133bcd154193b799d380331d5d3"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a75ef35f2df54ad55dbf4b73fe1da96f370e51b10c91f08b19603c64004acc"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e2e9839e14dd5308ee773c97115f1e0a1cb1d75cbeeee9f33824fa5144c7634"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44e65da1de4403d0576473e2344828ef9c4c6244d65cf4b75549bb46d40b8dd"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d847e4cde6ecc19125ccbc9bfac4a7ab37c234dd88fbb3c5c524e8e14da543"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c7a815258e5895d8900aec4454f38dca9aed71085f227537208057853f9d13f2"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8b929b9bd7cd7c3939f8bcfffa92fae7480bd1aa425279d51a89327d600c704d"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5db3a5b833764280ed7618393832e0853e40f3d3e9aa128ac0ba0f8278d08649"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:a0215ce6041d501f3155dc219712bc41252d0ab76474615b9700d63d4d9292af"},
|
||||||
{file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fd1ed388ea7fbed22c4968dd64bab0198de60750a25fe8c0c9d4bef5abe13824"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-win32.whl", hash = "sha256:6e6783bcc45f397fdebc118d772103d751b54cddf5b60fbcc958382d7dd64f3e"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
|
{file = "aiohttp-3.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:b5411d82cddd212644cf9360879eb5080f0d5f7d809d03262c50dad02f01421a"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:01d4c0c874aa4ddfb8098e85d10b5e875a70adc63db91f1ae65a4b04d3344cda"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5980a746d547a6ba173fd5ee85ce9077e72d118758db05d229044b469d9029a"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a482e6da906d5e6e653be079b29bc173a48e381600161c9932d89dfae5942ef"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80bd372b8d0715c66c974cf57fe363621a02f359f1ec81cba97366948c7fc873"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1161b345c0a444ebcf46bf0a740ba5dcf50612fd3d0528883fdc0eff578006a"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd56db019015b6acfaaf92e1ac40eb8434847d9bf88b4be4efe5bfd260aee692"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:153c2549f6c004d2754cc60603d4668899c9895b8a89397444a9c4efa282aaf4"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4a01951fabc4ce26ab791da5f3f24dca6d9a6f24121746eb19756416ff2d881b"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bfb9162dcf01f615462b995a516ba03e769de0789de1cadc0f916265c257e5d8"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7dde0009408969a43b04c16cbbe252c4f5ef4574ac226bc8815cd7342d2028b6"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4149d34c32f9638f38f544b3977a4c24052042affa895352d3636fa8bffd030a"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-win32.whl", hash = "sha256:68c5a82c8779bdfc6367c967a4a1b2aa52cd3595388bf5961a62158ee8a59e22"},
|
||||||
{file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
|
{file = "aiohttp-3.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2cf57fb50be5f52bda004b8893e63b48530ed9f0d6c96c84620dc92fe3cd9b9d"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
|
{file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eca4bf3734c541dc4f374ad6010a68ff6c6748f00451707f39857f429ca36ced"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
|
{file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1274477e4c71ce8cfe6c1ec2f806d57c015ebf84d83373676036e256bc55d690"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
|
{file = "aiohttp-3.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c543e54710d6158fc6f439296c7865b29e0b616629767e685a7185fab4a6b9"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
|
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910bec0c49637d213f5d9877105d26e0c4a4de2f8b1b29405ff37e9fc0ad52b8"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
|
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5443910d662db951b2e58eb70b0fbe6b6e2ae613477129a5805d0b66c54b6cb7"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
|
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e460be6978fc24e3df83193dc0cc4de46c9909ed92dd47d349a452ef49325b7"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
|
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb1558def481d84f03b45888473fc5a1f35747b5f334ef4e7a571bc0dfcb11f8"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
|
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34dd0c107799dcbbf7d48b53be761a013c0adf5571bf50c4ecad5643fe9cfcd0"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
|
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa1990247f02a54185dc0dff92a6904521172a22664c863a03ff64c42f9b5410"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
|
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0e584a10f204a617d71d359fe383406305a4b595b333721fa50b867b4a0a1548"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
|
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a3cf433f127efa43fee6b90ea4c6edf6c4a17109d1d037d1a52abec84d8f2e42"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
|
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c11f5b099adafb18e65c2c997d57108b5bbeaa9eeee64a84302c0978b1ec948b"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
|
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:84de26ddf621d7ac4c975dbea4c945860e08cccde492269db4e1538a6a6f3c35"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
|
{file = "aiohttp-3.8.5-cp38-cp38-win32.whl", hash = "sha256:ab88bafedc57dd0aab55fa728ea10c1911f7e4d8b43e1d838a1739f33712921c"},
|
||||||
{file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
|
{file = "aiohttp-3.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:5798a9aad1879f626589f3df0f8b79b3608a92e9beab10e5fda02c8a2c60db2e"},
|
||||||
{file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
|
{file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a6ce61195c6a19c785df04e71a4537e29eaa2c50fe745b732aa937c0c77169f3"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:773dd01706d4db536335fcfae6ea2440a70ceb03dd3e7378f3e815b03c97ab51"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f83a552443a526ea38d064588613aca983d0ee0038801bc93c0c916428310c28"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f7372f7341fcc16f57b2caded43e81ddd18df53320b6f9f042acad41f8e049a"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea353162f249c8097ea63c2169dd1aa55de1e8fecbe63412a9bc50816e87b761"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d47ae48db0b2dcf70bc8a3bc72b3de86e2a590fc299fdbbb15af320d2659de"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d827176898a2b0b09694fbd1088c7a31836d1a505c243811c87ae53a3f6273c1"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3562b06567c06439d8b447037bb655ef69786c590b1de86c7ab81efe1c9c15d8"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e874cbf8caf8959d2adf572a78bba17cb0e9d7e51bb83d86a3697b686a0ab4d"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6809a00deaf3810e38c628e9a33271892f815b853605a936e2e9e5129762356c"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:33776e945d89b29251b33a7e7d006ce86447b2cfd66db5e5ded4e5cd0340585c"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eaeed7abfb5d64c539e2db173f63631455f1196c37d9d8d873fc316470dfbacd"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e91d635961bec2d8f19dfeb41a539eb94bd073f075ca6dae6c8dc0ee89ad6f91"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-win32.whl", hash = "sha256:00ad4b6f185ec67f3e6562e8a1d2b69660be43070bd0ef6fcec5211154c7df67"},
|
||||||
|
{file = "aiohttp-3.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:c0a9034379a37ae42dea7ac1e048352d96286626251862e448933c0f59cbd79c"},
|
||||||
|
{file = "aiohttp-3.8.5.tar.gz", hash = "sha256:b9552ec52cc147dbf1944ac7ac98af7602e51ea2dcd076ed194ca3c0d1c7d0bc"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
aiosignal = ">=1.1.2"
|
aiosignal = ">=1.1.2"
|
||||||
async-timeout = ">=4.0.0a3,<5.0"
|
async-timeout = ">=4.0.0a3,<5.0"
|
||||||
attrs = ">=17.3.0"
|
attrs = ">=17.3.0"
|
||||||
charset-normalizer = ">=2.0,<3.0"
|
charset-normalizer = ">=2.0,<4.0"
|
||||||
frozenlist = ">=1.1.1"
|
frozenlist = ">=1.1.1"
|
||||||
multidict = ">=4.5,<7.0"
|
multidict = ">=4.5,<7.0"
|
||||||
yarl = ">=1.0,<2.0"
|
yarl = ">=1.0,<2.0"
|
||||||
@ -258,13 +273,13 @@ zstd = ["zstandard"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2022.12.7"
|
version = "2023.7.22"
|
||||||
description = "Python package for providing Mozilla's CA Bundle."
|
description = "Python package for providing Mozilla's CA Bundle."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
files = [
|
files = [
|
||||||
{file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
|
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
|
||||||
{file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
|
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -493,30 +508,34 @@ toml = ["tomli"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptography"
|
name = "cryptography"
|
||||||
version = "41.0.0"
|
version = "41.0.4"
|
||||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8"},
|
{file = "cryptography-41.0.4-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0"},
|
{file = "cryptography-41.0.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d"},
|
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46"},
|
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237"},
|
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4"},
|
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75"},
|
{file = "cryptography-41.0.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d"},
|
{file = "cryptography-41.0.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-win32.whl", hash = "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928"},
|
{file = "cryptography-41.0.4-cp37-abi3-win32.whl", hash = "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd"},
|
||||||
{file = "cryptography-41.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be"},
|
{file = "cryptography-41.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d"},
|
||||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5"},
|
{file = "cryptography-41.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67"},
|
||||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb"},
|
{file = "cryptography-41.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e"},
|
||||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be"},
|
{file = "cryptography-41.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829"},
|
||||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9"},
|
{file = "cryptography-41.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca"},
|
||||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2"},
|
{file = "cryptography-41.0.4-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d"},
|
||||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d"},
|
{file = "cryptography-41.0.4-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac"},
|
||||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895"},
|
{file = "cryptography-41.0.4-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9"},
|
||||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55"},
|
{file = "cryptography-41.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f"},
|
||||||
{file = "cryptography-41.0.0.tar.gz", hash = "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78"},
|
{file = "cryptography-41.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91"},
|
||||||
|
{file = "cryptography-41.0.4-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8"},
|
||||||
|
{file = "cryptography-41.0.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6"},
|
||||||
|
{file = "cryptography-41.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311"},
|
||||||
|
{file = "cryptography-41.0.4.tar.gz", hash = "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@ -545,13 +564,13 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django"
|
name = "django"
|
||||||
version = "4.1.10"
|
version = "4.1.13"
|
||||||
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
|
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "Django-4.1.10-py3-none-any.whl", hash = "sha256:26d0260c2fb8121009e62ffc548b2398dea2522b6454208a852fb0ef264c206c"},
|
{file = "Django-4.1.13-py3-none-any.whl", hash = "sha256:04ab3f6f46d084a0bba5a2c9a93a3a2eb3fe81589512367a75f79ee8acf790ce"},
|
||||||
{file = "Django-4.1.10.tar.gz", hash = "sha256:56343019a9fd839e2e5bf203daf45f25af79d5bffa4c71d56eae4f4404d82ade"},
|
{file = "Django-4.1.13.tar.gz", hash = "sha256:94a3f471e833c8f124ee7a2de11e92f633991d975e3fa5bdd91e8abd66426318"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "shynet"
|
name = "shynet"
|
||||||
version = "0.13.0"
|
version = "0.13.1"
|
||||||
description = "Modern, privacy-friendly, and cookie-free web analytics."
|
description = "Modern, privacy-friendly, and cookie-free web analytics."
|
||||||
authors = ["R. Miles McCain <github@sendmiles.email>"]
|
authors = ["R. Miles McCain <github@sendmiles.email>"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
29
shynet/a17t/locale/zh_TW/LC_MESSAGES/django.po
Normal file
29
shynet/a17t/locale/zh_TW/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2022-06-24 13:20+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: a17t/templates/a17t/includes/pagination.html:5
|
||||||
|
#: a17t/templates/a17t/includes/pagination.html:7
|
||||||
|
msgid "Previous"
|
||||||
|
msgstr "上一頁"
|
||||||
|
|
||||||
|
#: a17t/templates/a17t/includes/pagination.html:10
|
||||||
|
#: a17t/templates/a17t/includes/pagination.html:12
|
||||||
|
msgid "Next"
|
||||||
|
msgstr "下一頁"
|
118
shynet/analytics/locale/zh_TW/LC_MESSAGES/django.po
Normal file
118
shynet/analytics/locale/zh_TW/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2022-06-24 13:20+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: analytics/models.py:18
|
||||||
|
msgid "Service"
|
||||||
|
msgstr "服務"
|
||||||
|
|
||||||
|
#: analytics/models.py:24
|
||||||
|
msgid "Identifier"
|
||||||
|
msgstr "識別碼"
|
||||||
|
|
||||||
|
#: analytics/models.py:29
|
||||||
|
msgid "Start time"
|
||||||
|
msgstr "開始時間"
|
||||||
|
|
||||||
|
#: analytics/models.py:32
|
||||||
|
msgid "Last seen"
|
||||||
|
msgstr "最後瀏覽"
|
||||||
|
|
||||||
|
#: analytics/models.py:36
|
||||||
|
msgid "User agent"
|
||||||
|
msgstr "使用者代理程式"
|
||||||
|
|
||||||
|
#: analytics/models.py:37
|
||||||
|
msgid "Browser"
|
||||||
|
msgstr "瀏覽器"
|
||||||
|
|
||||||
|
#: analytics/models.py:38
|
||||||
|
msgid "Device"
|
||||||
|
msgstr "裝置"
|
||||||
|
|
||||||
|
#: analytics/models.py:42
|
||||||
|
msgid "Phone"
|
||||||
|
msgstr "手機"
|
||||||
|
|
||||||
|
#: analytics/models.py:43
|
||||||
|
msgid "Tablet"
|
||||||
|
msgstr "平板"
|
||||||
|
|
||||||
|
#: analytics/models.py:44
|
||||||
|
msgid "Desktop"
|
||||||
|
msgstr "桌上型電腦"
|
||||||
|
|
||||||
|
#: analytics/models.py:45
|
||||||
|
msgid "Robot"
|
||||||
|
msgstr "機器人"
|
||||||
|
|
||||||
|
#: analytics/models.py:46
|
||||||
|
msgid "Other"
|
||||||
|
msgstr "其他"
|
||||||
|
|
||||||
|
#: analytics/models.py:49
|
||||||
|
msgid "Device type"
|
||||||
|
msgstr "裝置類型"
|
||||||
|
|
||||||
|
#: analytics/models.py:51
|
||||||
|
msgid "OS"
|
||||||
|
msgstr "作業系統"
|
||||||
|
|
||||||
|
#: analytics/models.py:52
|
||||||
|
msgid "IP"
|
||||||
|
msgstr "IP"
|
||||||
|
|
||||||
|
#: analytics/models.py:55
|
||||||
|
msgid "Asn"
|
||||||
|
msgstr "ASN"
|
||||||
|
|
||||||
|
#: analytics/models.py:56
|
||||||
|
msgid "Country"
|
||||||
|
msgstr "國家"
|
||||||
|
|
||||||
|
#: analytics/models.py:57
|
||||||
|
msgid "Longitude"
|
||||||
|
msgstr "經度"
|
||||||
|
|
||||||
|
#: analytics/models.py:58
|
||||||
|
msgid "Latitude"
|
||||||
|
msgstr "緯度"
|
||||||
|
|
||||||
|
#: analytics/models.py:59
|
||||||
|
msgid "Time zone"
|
||||||
|
msgstr "時區"
|
||||||
|
|
||||||
|
#: analytics/models.py:61
|
||||||
|
msgid "Is bounce"
|
||||||
|
msgstr "是否為跳出"
|
||||||
|
|
||||||
|
#: analytics/models.py:64 analytics/models.py:100
|
||||||
|
msgid "Session"
|
||||||
|
msgstr "工作階段"
|
||||||
|
|
||||||
|
#: analytics/models.py:65
|
||||||
|
msgid "Sessions"
|
||||||
|
msgstr "工作階段次數"
|
||||||
|
|
||||||
|
#: analytics/models.py:122
|
||||||
|
msgid "Hit"
|
||||||
|
msgstr "點選"
|
||||||
|
|
||||||
|
#: analytics/models.py:123
|
||||||
|
msgid "Hits"
|
||||||
|
msgstr "點選次數"
|
@ -1,7 +1,10 @@
|
|||||||
from django.http import JsonResponse
|
from http import HTTPStatus
|
||||||
from django.contrib.auth.models import AnonymousUser
|
|
||||||
|
|
||||||
from core.models import User
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
from django.http import JsonResponse
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
class ApiTokenRequiredMixin:
|
class ApiTokenRequiredMixin:
|
||||||
@ -11,13 +14,13 @@ class ApiTokenRequiredMixin:
|
|||||||
return AnonymousUser()
|
return AnonymousUser()
|
||||||
|
|
||||||
token = token.split(" ")[1]
|
token = token.split(" ")[1]
|
||||||
user = User.objects.filter(api_token=token).first()
|
user: User = User.objects.filter(api_token=token).first()
|
||||||
|
return user or AnonymousUser()
|
||||||
return user if user else AnonymousUser()
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
request.user = self._get_user_by_token(request)
|
request.user = self._get_user_by_token(request)
|
||||||
if not request.user.is_authenticated:
|
return (
|
||||||
return JsonResponse(data={}, status=403)
|
super().dispatch(request, *args, **kwargs)
|
||||||
|
if request.user.is_authenticated
|
||||||
return super().dispatch(request, *args, **kwargs)
|
else JsonResponse(data={}, status=HTTPStatus.FORBIDDEN)
|
||||||
|
)
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
0
shynet/api/tests/__init__.py
Normal file
0
shynet/api/tests/__init__.py
Normal file
77
shynet/api/tests/test_mixins.py
Normal file
77
shynet/api/tests/test_mixins.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
from django.test import TestCase, RequestFactory
|
||||||
|
from django.views import View
|
||||||
|
|
||||||
|
from api.mixins import ApiTokenRequiredMixin
|
||||||
|
from core.factories import UserFactory
|
||||||
|
from core.models import _default_api_token, Service
|
||||||
|
|
||||||
|
|
||||||
|
class TestApiTokenRequiredMixin(TestCase):
|
||||||
|
class DummyView(ApiTokenRequiredMixin, View):
|
||||||
|
model = Service
|
||||||
|
template_name = "dashboard/pages/service.html"
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.user = UserFactory()
|
||||||
|
self.request = RequestFactory().get("/fake-path")
|
||||||
|
|
||||||
|
# Setup request and view.
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
self.view = self.DummyView()
|
||||||
|
|
||||||
|
def test_get_user_by_token_without_authorization_token(self):
|
||||||
|
"""
|
||||||
|
GIVEN: A request without Authorization header
|
||||||
|
WHEN: get_user_by_token is called
|
||||||
|
THEN: It should return AnonymousUser
|
||||||
|
"""
|
||||||
|
user = self.view._get_user_by_token(self.request)
|
||||||
|
|
||||||
|
self.assertEqual(user.is_anonymous, True)
|
||||||
|
|
||||||
|
def test_get_user_by_token_with_invalid_authorization_token(self):
|
||||||
|
"""
|
||||||
|
GIVEN: A request with invalid Authorization header
|
||||||
|
WHEN: get_user_by_token is called
|
||||||
|
THEN: It should return AnonymousUser
|
||||||
|
"""
|
||||||
|
self.request.META["HTTP_AUTHORIZATION"] = "Bearer invalid-token"
|
||||||
|
user = self.view._get_user_by_token(self.request)
|
||||||
|
|
||||||
|
self.assertEqual(user.is_anonymous, True)
|
||||||
|
|
||||||
|
def test_get_user_by_token_with_invalid_token(self):
|
||||||
|
"""
|
||||||
|
GIVEN: A request with invalid token
|
||||||
|
WHEN: get_user_by_token is called
|
||||||
|
THEN: It should return AnonymousUser
|
||||||
|
"""
|
||||||
|
self.request.META["HTTP_AUTHORIZATION"] = f"Token {_default_api_token()}"
|
||||||
|
user = self.view._get_user_by_token(self.request)
|
||||||
|
|
||||||
|
self.assertEqual(user.is_anonymous, True)
|
||||||
|
|
||||||
|
def test_get_user_by_token_with_valid_token(self):
|
||||||
|
"""
|
||||||
|
GIVEN: A request with valid token
|
||||||
|
WHEN: get_user_by_token is called
|
||||||
|
THEN: It should return the user
|
||||||
|
"""
|
||||||
|
self.request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
|
||||||
|
user = self.view._get_user_by_token(self.request)
|
||||||
|
|
||||||
|
self.assertEqual(user, self.user)
|
||||||
|
|
||||||
|
def test_dispatch_with_unauthenticated_user(self):
|
||||||
|
"""
|
||||||
|
GIVEN: A request with unauthenticated user
|
||||||
|
WHEN: dispatch is called
|
||||||
|
THEN: It should return 403
|
||||||
|
"""
|
||||||
|
self.request.META["HTTP_AUTHORIZATION"] = f"Token {_default_api_token()}"
|
||||||
|
response = self.view.dispatch(self.request)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN)
|
79
shynet/api/tests/test_views.py
Normal file
79
shynet/api/tests/test_views.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import json
|
||||||
|
from http import HTTPStatus
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.test import TestCase, RequestFactory
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from api.views import DashboardApiView
|
||||||
|
from core.factories import UserFactory, ServiceFactory
|
||||||
|
from core.models import Service
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class TestDashboardApiView(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
super().setUp()
|
||||||
|
self.user: User = UserFactory()
|
||||||
|
self.service_1: Service = ServiceFactory(owner=self.user)
|
||||||
|
self.service_2: Service = ServiceFactory(owner=self.user)
|
||||||
|
self.url = reverse("api:services")
|
||||||
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
|
def test_get_with_unauthenticated_user(self):
|
||||||
|
"""
|
||||||
|
GIVEN: An unauthenticated user
|
||||||
|
WHEN: The user makes a GET request to the dashboard API view
|
||||||
|
THEN: It should return 403
|
||||||
|
"""
|
||||||
|
response = self.client.get(self.url)
|
||||||
|
self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN)
|
||||||
|
|
||||||
|
def test_get_returns_400(self):
|
||||||
|
"""
|
||||||
|
GIVEN: An authenticated user
|
||||||
|
WHEN: The user makes a GET request to the dashboard API view with an invalid date format
|
||||||
|
THEN: It should return 400
|
||||||
|
"""
|
||||||
|
request = self.factory.get(self.url, {"startDate": "01/01/2000"})
|
||||||
|
request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
|
||||||
|
|
||||||
|
response = DashboardApiView.as_view()(request)
|
||||||
|
self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
|
data = json.loads(response.content)
|
||||||
|
self.assertEqual(data["error"], "Invalid date format. Use YYYY-MM-DD.")
|
||||||
|
|
||||||
|
def test_get_with_authenticated_user(self):
|
||||||
|
"""
|
||||||
|
GIVEN: An authenticated user
|
||||||
|
WHEN: The user makes a GET request to the dashboard API view
|
||||||
|
THEN: It should return 200
|
||||||
|
"""
|
||||||
|
request = self.factory.get(self.url)
|
||||||
|
request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
|
||||||
|
|
||||||
|
response = DashboardApiView.as_view()(request)
|
||||||
|
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||||
|
|
||||||
|
data = json.loads(response.content)
|
||||||
|
self.assertEqual(len(data["services"]), 2)
|
||||||
|
|
||||||
|
def test_get_with_service_uuid(self):
|
||||||
|
"""
|
||||||
|
GIVEN: An authenticated user
|
||||||
|
WHEN: The user makes a GET request to the dashboard API view with a service UUID
|
||||||
|
THEN: It should return 200 and a single service
|
||||||
|
"""
|
||||||
|
request = self.factory.get(self.url, {"uuid": str(self.service_1.uuid)})
|
||||||
|
request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
|
||||||
|
|
||||||
|
response = DashboardApiView.as_view()(request)
|
||||||
|
self.assertEqual(response.status_code, HTTPStatus.OK)
|
||||||
|
|
||||||
|
data = json.loads(response.content)
|
||||||
|
self.assertEqual(len(data["services"]), 1)
|
||||||
|
self.assertEqual(data["services"][0]["uuid"], str(self.service_1.uuid))
|
||||||
|
self.assertEqual(data["services"][0]["name"], str(self.service_1.name))
|
||||||
|
|
@ -1,54 +1,46 @@
|
|||||||
import uuid
|
from http import HTTPStatus
|
||||||
from django.http import JsonResponse
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.db.models.query import QuerySet
|
from django.db.models.query import QuerySet
|
||||||
|
from django.http import JsonResponse
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
|
|
||||||
from dashboard.mixins import DateRangeMixin
|
|
||||||
from core.models import Service
|
from core.models import Service
|
||||||
|
from core.utils import is_valid_uuid
|
||||||
|
from dashboard.mixins import DateRangeMixin
|
||||||
from .mixins import ApiTokenRequiredMixin
|
from .mixins import ApiTokenRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
def is_valid_uuid(value):
|
|
||||||
try:
|
|
||||||
uuid.UUID(value)
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class DashboardApiView(ApiTokenRequiredMixin, DateRangeMixin, View):
|
class DashboardApiView(ApiTokenRequiredMixin, DateRangeMixin, View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
services = Service.objects.filter(
|
services = Service.objects.filter(Q(owner=request.user) | Q(collaborators__in=[request.user])).distinct()
|
||||||
Q(owner=request.user) | Q(collaborators__in=[request.user])
|
|
||||||
).distinct()
|
|
||||||
|
|
||||||
uuid = request.GET.get("uuid")
|
uuid_ = request.GET.get("uuid")
|
||||||
if uuid and is_valid_uuid(uuid):
|
if uuid_ and is_valid_uuid(uuid_):
|
||||||
services = services.filter(uuid=uuid)
|
services = services.filter(uuid=uuid_)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
start = self.get_start_date()
|
start = self.get_start_date()
|
||||||
end = self.get_end_date()
|
end = self.get_end_date()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return JsonResponse(status=400, data={"error": "Invalid date format"})
|
return JsonResponse(status=HTTPStatus.BAD_REQUEST, data={"error": "Invalid date format. Use YYYY-MM-DD."})
|
||||||
|
|
||||||
|
service: Service
|
||||||
services_data = [
|
services_data = [
|
||||||
{
|
{
|
||||||
"name": s.name,
|
"name": service.name,
|
||||||
"uuid": s.uuid,
|
"uuid": service.uuid,
|
||||||
"link": s.link,
|
"link": service.link,
|
||||||
"stats": s.get_core_stats(start, end),
|
"stats": service.get_core_stats(start, end),
|
||||||
}
|
}
|
||||||
for s in services
|
for service in services
|
||||||
]
|
]
|
||||||
|
|
||||||
services_data = self._convert_querysets_to_lists(services_data)
|
services_data = self._convert_querysets_to_lists(services_data)
|
||||||
|
|
||||||
return JsonResponse(data={"services": services_data})
|
return JsonResponse(data={"services": services_data})
|
||||||
|
|
||||||
def _convert_querysets_to_lists(self, services_data):
|
def _convert_querysets_to_lists(self, services_data: list[dict]) -> list[dict]:
|
||||||
for service_data in services_data:
|
for service_data in services_data:
|
||||||
for key, value in service_data["stats"].items():
|
for key, value in service_data["stats"].items():
|
||||||
if isinstance(value, QuerySet):
|
if isinstance(value, QuerySet):
|
||||||
|
87
shynet/core/locale/zh_TW/LC_MESSAGES/django.po
Normal file
87
shynet/core/locale/zh_TW/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2022-06-24 13:20+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: core/models.py:58
|
||||||
|
msgid "Active"
|
||||||
|
msgstr "啟用"
|
||||||
|
|
||||||
|
#: core/models.py:58
|
||||||
|
msgid "Archived"
|
||||||
|
msgstr "已封存"
|
||||||
|
|
||||||
|
#: core/models.py:61
|
||||||
|
msgid "Name"
|
||||||
|
msgstr "名稱"
|
||||||
|
|
||||||
|
#: core/models.py:63
|
||||||
|
msgid "Owner"
|
||||||
|
msgstr "擁有者"
|
||||||
|
|
||||||
|
#: core/models.py:67
|
||||||
|
msgid "Collaborators"
|
||||||
|
msgstr "協作者"
|
||||||
|
|
||||||
|
#: core/models.py:70
|
||||||
|
msgid "created"
|
||||||
|
msgstr "已建立"
|
||||||
|
|
||||||
|
#: core/models.py:71
|
||||||
|
msgid "link"
|
||||||
|
msgstr "連結"
|
||||||
|
|
||||||
|
#: core/models.py:72
|
||||||
|
msgid "origins"
|
||||||
|
msgstr "來源"
|
||||||
|
|
||||||
|
#: core/models.py:75
|
||||||
|
msgid "status"
|
||||||
|
msgstr "狀態"
|
||||||
|
|
||||||
|
#: core/models.py:77
|
||||||
|
msgid "Respect dnt"
|
||||||
|
msgstr "尊重停止追蹤 (Do Not Track) 設定"
|
||||||
|
|
||||||
|
#: core/models.py:78
|
||||||
|
msgid "Ignore robots"
|
||||||
|
msgstr "忽略機器人"
|
||||||
|
|
||||||
|
#: core/models.py:79
|
||||||
|
msgid "Collect ips"
|
||||||
|
msgstr "收集 IP"
|
||||||
|
|
||||||
|
#: core/models.py:82
|
||||||
|
msgid "Igored ips"
|
||||||
|
msgstr "忽略的 IP"
|
||||||
|
|
||||||
|
#: core/models.py:86
|
||||||
|
msgid "Hide referrer regex"
|
||||||
|
msgstr "隱藏來源參照正規表達式"
|
||||||
|
|
||||||
|
#: core/models.py:88
|
||||||
|
msgid "Script inject"
|
||||||
|
msgstr "插入指令碼"
|
||||||
|
|
||||||
|
#: core/models.py:91
|
||||||
|
msgid "Service"
|
||||||
|
msgstr "服務"
|
||||||
|
|
||||||
|
#: core/models.py:92
|
||||||
|
msgid "Services"
|
||||||
|
msgstr "服務"
|
10
shynet/core/utils.py
Normal file
10
shynet/core/utils.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_uuid(value: str) -> bool:
|
||||||
|
"""Check if a string is a valid UUID."""
|
||||||
|
try:
|
||||||
|
uuid.UUID(value)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
659
shynet/dashboard/locale/zh_TW/LC_MESSAGES/django.po
Normal file
659
shynet/dashboard/locale/zh_TW/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,659 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2022-06-24 13:43+0200\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
|
#: forms.py:28 forms.py:29 forms.py:30 forms.py:59
|
||||||
|
msgid "Yes"
|
||||||
|
msgstr "是"
|
||||||
|
|
||||||
|
#: forms.py:28 forms.py:29 forms.py:30 forms.py:59
|
||||||
|
msgid "No"
|
||||||
|
msgstr "否"
|
||||||
|
|
||||||
|
#: forms.py:35
|
||||||
|
msgid "Allowed origins"
|
||||||
|
msgstr "允許的來源"
|
||||||
|
|
||||||
|
#: forms.py:36
|
||||||
|
msgid "Respect DNT"
|
||||||
|
msgstr "尊重停止追蹤 (Do Not Track) 設定"
|
||||||
|
|
||||||
|
#: forms.py:37
|
||||||
|
msgid "Ignored IP addresses"
|
||||||
|
msgstr "忽略的 IP"
|
||||||
|
|
||||||
|
#: forms.py:38
|
||||||
|
msgid "Ignore robots"
|
||||||
|
msgstr "忽略機器人"
|
||||||
|
|
||||||
|
#: forms.py:39
|
||||||
|
msgid "Hide specific referrers"
|
||||||
|
msgstr "隱藏特定的參照來源"
|
||||||
|
|
||||||
|
#: forms.py:40
|
||||||
|
msgid "Additional injected JS"
|
||||||
|
msgstr "額外插入的 JS"
|
||||||
|
|
||||||
|
#: forms.py:43
|
||||||
|
msgid "What should the service be called?"
|
||||||
|
msgstr "這項服務應該被稱為什麼?"
|
||||||
|
|
||||||
|
#: forms.py:44
|
||||||
|
msgid "What's the service's primary URL?"
|
||||||
|
msgstr "服務的主要 URL 是什麼?"
|
||||||
|
|
||||||
|
#: forms.py:46
|
||||||
|
msgid ""
|
||||||
|
"At what origins does the service operate? Use commas to separate multiple "
|
||||||
|
"values. This sets CORS headers, so use '*' if you're not sure (or don't "
|
||||||
|
"care)."
|
||||||
|
msgstr ""
|
||||||
|
"服務在哪些來源運作?使用逗號分隔多個值。這設定了 CORS 標頭,所以如果你不確定(或不在乎),請使用 '*'。"
|
||||||
|
|
||||||
|
#: forms.py:48
|
||||||
|
msgid ""
|
||||||
|
"Should visitors who have enabled <a href='https://en.wikipedia.org/wiki/"
|
||||||
|
"Do_Not_Track'>Do Not Track</a> be excluded from all data?"
|
||||||
|
msgstr ""
|
||||||
|
"是否應排除已啟用 <a href='https://en.wikipedia.org/wiki/Do_Not_Track'>停止追蹤 (Do Not Track)</a> 的訪客的所有資料?"
|
||||||
|
|
||||||
|
#: forms.py:49
|
||||||
|
msgid ""
|
||||||
|
"A comma-separated list of IP addresses or IP ranges (IPv4 and IPv6) to "
|
||||||
|
"exclude from tracking (e.g., '192.168.0.2, 127.0.0.1/32')."
|
||||||
|
msgstr ""
|
||||||
|
"要從追蹤中排除的 IP 位址或 IP 範圍(IPv4 和 IPv6)的逗號分隔列表(例如,'192.168.0.2, 127.0.0.1/32')。"
|
||||||
|
|
||||||
|
#: forms.py:50
|
||||||
|
msgid "Should sessions generated by bots be excluded from tracking?"
|
||||||
|
msgstr "是否應排除由機器人產生的工作階段從追蹤中?"
|
||||||
|
|
||||||
|
#: forms.py:51
|
||||||
|
msgid ""
|
||||||
|
"Any referrers that match this <a href='https://regexr.com/'>RegEx</a> will "
|
||||||
|
"not be listed in the referrer summary. Sessions will still be tracked "
|
||||||
|
"normally. No effect if left blank."
|
||||||
|
msgstr ""
|
||||||
|
"任何符合此 <a href='https://regexr.com/'>RegEx</a> 的參照來源將不會在參照來源摘要中列出。工作階段仍將正常追蹤。如果留空則無效果。"
|
||||||
|
|
||||||
|
#: forms.py:52
|
||||||
|
msgid ""
|
||||||
|
"Optional additional JavaScript to inject at the end of the Shynet script. "
|
||||||
|
"This code will be injected on every page where this service is installed."
|
||||||
|
msgstr ""
|
||||||
|
"選擇性的額外 JavaScript,插入到 Shynet 指令碼的末端。此程式碼將插入到安裝此服務的每個頁面上。"
|
||||||
|
|
||||||
|
#: forms.py:56
|
||||||
|
msgid "IP address collection is disabled globally by your administrator."
|
||||||
|
msgstr "您的管理員已全域停用 IP 收集。"
|
||||||
|
|
||||||
|
#: forms.py:58
|
||||||
|
msgid ""
|
||||||
|
"Should individual IP addresses be collected? IP metadata (location, host, "
|
||||||
|
"etc) will still be collected."
|
||||||
|
msgstr "是否應收集個別 IP ?仍將收集 IP 中繼資料(位置、主機等)。"
|
||||||
|
|
||||||
|
#: forms.py:71
|
||||||
|
msgid ""
|
||||||
|
"Which users on this Shynet instance should have read-only access to this "
|
||||||
|
"service? (Comma separated list of emails.)"
|
||||||
|
msgstr ""
|
||||||
|
"此 Shynet 服務上的哪些使用者應具有對此服務的唯讀存取權限?(電子郵件的逗號分隔列表。)"
|
||||||
|
|
||||||
|
#: templates/account/account_inactive.html:5
|
||||||
|
#: templates/account/account_inactive.html:6
|
||||||
|
msgid "Account Inactive"
|
||||||
|
msgstr "帳戶不活躍"
|
||||||
|
|
||||||
|
#: templates/account/account_inactive.html:9
|
||||||
|
msgid "This account is inactive."
|
||||||
|
msgstr "此帳戶不活躍。"
|
||||||
|
|
||||||
|
#: templates/account/email.html:5 templates/account/email.html:6
|
||||||
|
msgid "Email Addresses"
|
||||||
|
msgstr "電子郵件地址"
|
||||||
|
|
||||||
|
#: templates/account/email.html:12
|
||||||
|
msgid "These are your known email addresses:"
|
||||||
|
msgstr "這些是您的已知電子郵件地址:"
|
||||||
|
|
||||||
|
#: templates/account/email.html:26
|
||||||
|
msgid "Verified"
|
||||||
|
msgstr "已驗證"
|
||||||
|
|
||||||
|
#: templates/account/email.html:28
|
||||||
|
msgid "Unverified"
|
||||||
|
msgstr "未驗證"
|
||||||
|
|
||||||
|
#: templates/account/email.html:30
|
||||||
|
msgid "Primary"
|
||||||
|
msgstr "主要"
|
||||||
|
|
||||||
|
#: templates/account/email.html:36
|
||||||
|
msgid "Make Primary"
|
||||||
|
msgstr "設為預設"
|
||||||
|
|
||||||
|
#: templates/account/email.html:37
|
||||||
|
msgid "Resend Verification"
|
||||||
|
msgstr "重新傳送驗證"
|
||||||
|
|
||||||
|
#: templates/account/email.html:38
|
||||||
|
msgid "Remove"
|
||||||
|
msgstr "移除"
|
||||||
|
|
||||||
|
#: templates/account/email.html:46
|
||||||
|
msgid ""
|
||||||
|
"You currently do not have an email address associated with your account. "
|
||||||
|
"Without one, you won't be able to reset your password, receive "
|
||||||
|
"notifications, etc."
|
||||||
|
msgstr ""
|
||||||
|
"您目前的帳戶沒有關聯的電子郵件地址。沒有它,您將無法重設密碼、接收通知等。"
|
||||||
|
|
||||||
|
#: templates/account/email.html:57
|
||||||
|
msgid "Add Address"
|
||||||
|
msgstr "新增地址"
|
||||||
|
|
||||||
|
#: templates/account/email.html:66
|
||||||
|
msgid "Do you really want to remove the selected email address?"
|
||||||
|
msgstr "您真的要移除選定的電子郵件地址嗎?"
|
||||||
|
|
||||||
|
#: templates/account/email/email_confirmation_message.txt:1
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Hi there,\n"
|
||||||
|
"\n"
|
||||||
|
"You're receiving this email because %(user_display)s has listed this email "
|
||||||
|
"as a valid contact address for their account.\n"
|
||||||
|
"\n"
|
||||||
|
"To confirm this is correct, go to %(activate_url)s\n"
|
||||||
|
msgstr ""
|
||||||
|
"您好,\n"
|
||||||
|
"\n"
|
||||||
|
"您收到此電子郵件是因為 %(user_display)s 已將此電子郵件列為其帳戶的有效聯絡地址。\n"
|
||||||
|
"\n"
|
||||||
|
"要確認這是正確的,請前往 %(activate_url)s\n"
|
||||||
|
|
||||||
|
#: templates/account/email/email_confirmation_message.txt:7
|
||||||
|
#: templates/account/email/password_reset_key_message.txt:9
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Thank you,\n"
|
||||||
|
"%(site_name)s\n"
|
||||||
|
msgstr ""
|
||||||
|
"謝謝您,\n"
|
||||||
|
"%(site_name)s\n"
|
||||||
|
|
||||||
|
#: templates/account/email/email_confirmation_subject.txt:3
|
||||||
|
#: templates/account/email_confirm.html:6
|
||||||
|
#: templates/account/email_confirm.html:7
|
||||||
|
msgid "Confirm Email Address"
|
||||||
|
msgstr "確認電子郵件地址"
|
||||||
|
|
||||||
|
#: templates/account/email/password_reset_key_message.txt:1
|
||||||
|
msgid ""
|
||||||
|
"Hi there,\n"
|
||||||
|
"\n"
|
||||||
|
"You're receiving this email because you or someone else has requested a "
|
||||||
|
"password for your account.\n"
|
||||||
|
"\n"
|
||||||
|
"This message can be safely ignored if you did not request a password reset. "
|
||||||
|
"Click the link below to reset your password."
|
||||||
|
msgstr ""
|
||||||
|
"您好,\n"
|
||||||
|
"\n"
|
||||||
|
"您收到此電子郵件是因為您或其他人已為您的帳戶請求密碼。\n"
|
||||||
|
"\n"
|
||||||
|
"如果您未請求重設密碼,則可以安全地忽略此訊息。點選下面的連結來重設您的密碼。"
|
||||||
|
|
||||||
|
#: templates/account/email/password_reset_key_subject.txt:3
|
||||||
|
msgid "Password Reset Email"
|
||||||
|
msgstr "密碼重設電子郵件"
|
||||||
|
|
||||||
|
#: templates/account/email_confirm.html:15
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Please confirm that <a\n"
|
||||||
|
" href=\"mailto:%(email)s\">%(email)s</a> is a valid email where we "
|
||||||
|
"can reach you."
|
||||||
|
msgstr ""
|
||||||
|
"請確認 <a\n"
|
||||||
|
" href=\"mailto:%(email)s\">%(email)s</a> 是我們可以聯絡您的有效電子郵件。"
|
||||||
|
|
||||||
|
#: templates/account/email_confirm.html:21
|
||||||
|
msgid "Confirm"
|
||||||
|
msgstr "確認"
|
||||||
|
|
||||||
|
#: templates/account/email_confirm.html:28
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"This email confirmation link expired or is invalid. Please <a href="
|
||||||
|
"\"%(email_url)s\">issue a new\n"
|
||||||
|
" email confirmation request</a>."
|
||||||
|
msgstr ""
|
||||||
|
"此電子郵件確認連結已過期或無效。請<a href=\"%(email_url)s\">發出新的電子郵件確認請求</a>。"
|
||||||
|
|
||||||
|
#: templates/account/login.html:6 templates/account/login.html:7
|
||||||
|
#: templates/account/login.html:19
|
||||||
|
msgid "Sign In"
|
||||||
|
msgstr "登入"
|
||||||
|
|
||||||
|
#: templates/account/login.html:20 templates/account/password_reset.html:21
|
||||||
|
msgid "Reset Password"
|
||||||
|
msgstr "重設密碼"
|
||||||
|
|
||||||
|
#: templates/account/logout.html:5 templates/account/logout.html:6
|
||||||
|
#: templates/account/logout.html:16
|
||||||
|
msgid "Sign Out"
|
||||||
|
msgstr "登出"
|
||||||
|
|
||||||
|
#: templates/account/logout.html:9
|
||||||
|
msgid "Are you sure you want to sign out?"
|
||||||
|
msgstr "您確定要登出嗎?"
|
||||||
|
|
||||||
|
#: templates/account/messages/cannot_delete_primary_email.txt:2
|
||||||
|
#, python-format
|
||||||
|
msgid "You cannot remove your primary email address (%(email)s)."
|
||||||
|
msgstr "您不能移除您的主要電子郵件地址 (%(email)s)。"
|
||||||
|
|
||||||
|
#: templates/account/messages/email_confirmation_sent.txt:2
|
||||||
|
#, python-format
|
||||||
|
msgid "Confirmation email sent to %(email)s."
|
||||||
|
msgstr "確認電子郵件已傳送到 %(email)s。"
|
||||||
|
|
||||||
|
#: templates/account/messages/email_confirmed.txt:2
|
||||||
|
#, python-format
|
||||||
|
msgid "Confirmed %(email)s."
|
||||||
|
msgstr "已確認 %(email)s。"
|
||||||
|
|
||||||
|
#: templates/account/messages/email_deleted.txt:2
|
||||||
|
#, python-format
|
||||||
|
msgid "Removed email address %(email)s."
|
||||||
|
msgstr "已移除電子郵件地址 %(email)s。"
|
||||||
|
|
||||||
|
#: templates/account/messages/logged_in.txt:4
|
||||||
|
#, python-format
|
||||||
|
msgid "Successfully signed in as %(name)s."
|
||||||
|
msgstr "成功以 %(name)s 的身份登入。"
|
||||||
|
|
||||||
|
#: templates/account/messages/logged_out.txt:2
|
||||||
|
msgid "You have signed out."
|
||||||
|
msgstr "您已登出。"
|
||||||
|
|
||||||
|
#: templates/account/messages/password_changed.txt:2
|
||||||
|
msgid "Password successfully changed."
|
||||||
|
msgstr "密碼已成功更改。"
|
||||||
|
|
||||||
|
#: templates/account/messages/password_set.txt:2
|
||||||
|
msgid "Password successfully set."
|
||||||
|
msgstr "密碼已成功設定。"
|
||||||
|
|
||||||
|
#: templates/account/messages/primary_email_set.txt:2
|
||||||
|
msgid "New primary email address set."
|
||||||
|
msgstr "已設定新的主要電子郵件地址。"
|
||||||
|
|
||||||
|
#: templates/account/messages/unverified_primary_email.txt:2
|
||||||
|
msgid "Your primary email address must be verified."
|
||||||
|
msgstr "您的主要電子郵件地址必須經過驗證。"
|
||||||
|
|
||||||
|
#: templates/account/password_change.html:5
|
||||||
|
#: templates/account/password_change.html:6
|
||||||
|
#: templates/account/password_change.html:12
|
||||||
|
#: templates/account/password_reset_from_key.html:4
|
||||||
|
#: templates/account/password_reset_from_key.html:5
|
||||||
|
#: templates/account/password_reset_from_key.html:16
|
||||||
|
#: templates/account/password_reset_from_key_done.html:4
|
||||||
|
#: templates/account/password_reset_from_key_done.html:5
|
||||||
|
msgid "Change Password"
|
||||||
|
msgstr "更改密碼"
|
||||||
|
|
||||||
|
#: templates/account/password_reset.html:6
|
||||||
|
#: templates/account/password_reset.html:7
|
||||||
|
#: templates/account/password_reset_done.html:6
|
||||||
|
#: templates/account/password_reset_done.html:7
|
||||||
|
msgid "Password Reset"
|
||||||
|
msgstr "密碼重設"
|
||||||
|
|
||||||
|
#: templates/account/password_reset.html:15
|
||||||
|
msgid ""
|
||||||
|
"Forgotten your password? Enter your email address below, and we'll send you "
|
||||||
|
"an email to reset it."
|
||||||
|
msgstr ""
|
||||||
|
"忘記了您的密碼?在下面輸入您的電子郵件地址,我們將向您傳送電子郵件以重設它。"
|
||||||
|
|
||||||
|
#: templates/account/password_reset_done.html:14
|
||||||
|
msgid ""
|
||||||
|
"We have sent you an email with a password reset link. Please try again if "
|
||||||
|
"you do not receive it within a few minutes."
|
||||||
|
msgstr ""
|
||||||
|
"我們已向您發送了一封帶有密碼重設連結的電子郵件。如果您在幾分鐘內未收到,請再試一次。"
|
||||||
|
|
||||||
|
#: templates/account/password_reset_from_key.html:10
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"The password reset link was invalid, possibly because it has already been "
|
||||||
|
"used. Please request a <a href=\"%(passwd_reset_url)s\">new password reset</"
|
||||||
|
"a>."
|
||||||
|
msgstr ""
|
||||||
|
"密碼重設連結無效,可能是因為它已被使用。請請求<a href=\"%(passwd_reset_url)s\">新的密碼重設</a>。"
|
||||||
|
|
||||||
|
#: templates/account/password_reset_from_key.html:19
|
||||||
|
#: templates/account/password_reset_from_key_done.html:8
|
||||||
|
msgid "Your password is now changed."
|
||||||
|
msgstr "您的密碼現在已更改。"
|
||||||
|
|
||||||
|
#: templates/account/password_set.html:5 templates/account/password_set.html:6
|
||||||
|
#: templates/account/password_set.html:12
|
||||||
|
msgid "Set Password"
|
||||||
|
msgstr "設定密碼"
|
||||||
|
|
||||||
|
#: templates/account/signup.html:5 templates/account/signup.html:6
|
||||||
|
#: templates/account/signup.html:17
|
||||||
|
msgid "Sign Up"
|
||||||
|
msgstr "註冊"
|
||||||
|
|
||||||
|
#: templates/account/signup.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Already have an account? Then please <a href=\"%(login_url)s\">sign in</a> "
|
||||||
|
"instead."
|
||||||
|
msgstr ""
|
||||||
|
"已經有帳戶了嗎?那麼請<a href=\"%(login_url)s\">登入</a>。"
|
||||||
|
|
||||||
|
#: templates/account/signup_closed.html:5
|
||||||
|
#: templates/account/signup_closed.html:6
|
||||||
|
msgid "Sign Up Closed"
|
||||||
|
msgstr "註冊已關閉"
|
||||||
|
|
||||||
|
#: templates/account/signup_closed.html:9
|
||||||
|
msgid "Public sign-ups are not allowed at this time."
|
||||||
|
msgstr "目前不允許公開註冊。"
|
||||||
|
|
||||||
|
#: templates/account/snippets/already_logged_in.html:6
|
||||||
|
msgid "Note"
|
||||||
|
msgstr "注意"
|
||||||
|
|
||||||
|
#: templates/account/snippets/already_logged_in.html:6
|
||||||
|
#, python-format
|
||||||
|
msgid "you are already logged in as %(user_display)s."
|
||||||
|
msgstr "您已經以 %(user_display)s 的身份登入。"
|
||||||
|
|
||||||
|
#: templates/account/verification_sent.html:5
|
||||||
|
#: templates/account/verification_sent.html:6
|
||||||
|
#: templates/account/verified_email_required.html:5
|
||||||
|
#: templates/account/verified_email_required.html:6
|
||||||
|
msgid "Verify Email Address"
|
||||||
|
msgstr "驗證電子郵件地址"
|
||||||
|
|
||||||
|
#: templates/account/verification_sent.html:9
|
||||||
|
msgid ""
|
||||||
|
"We have sent an email to you for verification. Follow the link provided to "
|
||||||
|
"finalize the signup process. Please try to log in again if you do not "
|
||||||
|
"receive it within a few minutes."
|
||||||
|
msgstr ""
|
||||||
|
"我們已向您發送了一封驗證電子郵件。按照提供的連結完成註冊過程。如果您在幾分鐘內未收到,請再試一次登入。"
|
||||||
|
|
||||||
|
#: templates/account/verified_email_required.html:11
|
||||||
|
msgid ""
|
||||||
|
"This part of the site requires us to verify that\n"
|
||||||
|
"you are who you claim to be. For this purpose, we require that you\n"
|
||||||
|
"verify ownership of your email address. "
|
||||||
|
msgstr ""
|
||||||
|
"該網站的這一部分要求我們驗證\n"
|
||||||
|
"您是您聲稱的人。為此,我們要求您\n"
|
||||||
|
"驗證您的電子郵件地址的所有權。"
|
||||||
|
|
||||||
|
#: templates/account/verified_email_required.html:15
|
||||||
|
msgid ""
|
||||||
|
"We have sent an email to you for\n"
|
||||||
|
"verification. Please click on the link inside this email. Please\n"
|
||||||
|
"try again if you do not receive it within a few minutes."
|
||||||
|
msgstr ""
|
||||||
|
"我們已向您發送了一封驗證電子郵件。\n"
|
||||||
|
"請點選此電子郵件內的連結。請\n"
|
||||||
|
"如果您在幾分鐘內未收到,請再試一次。"
|
||||||
|
|
||||||
|
#: templates/account/verified_email_required.html:19
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your "
|
||||||
|
"email address</a>."
|
||||||
|
msgstr ""
|
||||||
|
"<strong>注意:</strong>您仍然可以<a href=\"%(email_url)s\">更改您的電子郵件地址</a>。"
|
||||||
|
|
||||||
|
#: templates/base.html:47
|
||||||
|
msgid "Services"
|
||||||
|
msgstr "服務"
|
||||||
|
|
||||||
|
#: templates/base.html:60
|
||||||
|
msgid "+ Create"
|
||||||
|
msgstr "+ 建立"
|
||||||
|
|
||||||
|
#: templates/base.html:69
|
||||||
|
msgid "Collaborations"
|
||||||
|
msgstr "合作"
|
||||||
|
|
||||||
|
#: templates/base.html:81
|
||||||
|
msgid "Account"
|
||||||
|
msgstr "帳戶"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/service_form.html:8
|
||||||
|
msgid "Advanced settings"
|
||||||
|
msgstr "進階設定"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/service_overview.html:15
|
||||||
|
#: templates/dashboard/pages/service_session_list.html:5
|
||||||
|
msgid "Sessions"
|
||||||
|
msgstr "工作階段"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/service_overview.html:22
|
||||||
|
#: templates/dashboard/includes/session_list.html:9
|
||||||
|
#: templates/dashboard/pages/service.html:38
|
||||||
|
#: templates/dashboard/pages/service.html:110
|
||||||
|
msgid "Hits"
|
||||||
|
msgstr "點選次數"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/service_overview.html:29
|
||||||
|
#: templates/dashboard/pages/service.html:60
|
||||||
|
msgid "Bounce Rate"
|
||||||
|
msgstr "跳出率"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/service_overview.html:40
|
||||||
|
msgid "Avg. Duration"
|
||||||
|
msgstr "平均持續時間"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/session_list.html:5
|
||||||
|
msgid "Session Start"
|
||||||
|
msgstr "工作階段開始"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/session_list.html:6
|
||||||
|
msgid "Identity"
|
||||||
|
msgstr "身份"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/session_list.html:7
|
||||||
|
#: templates/dashboard/pages/service.html:73
|
||||||
|
#: templates/dashboard/pages/service_session.html:81
|
||||||
|
msgid "Duration"
|
||||||
|
msgstr "持續時間"
|
||||||
|
|
||||||
|
#: templates/dashboard/includes/session_list.html:36
|
||||||
|
msgid "No data yet"
|
||||||
|
msgstr "尚無資料"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/dashboard.html:16
|
||||||
|
msgid "New Service"
|
||||||
|
msgstr "新服務"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/index.html:6
|
||||||
|
#: templates/dashboard/pages/service_session_list.html:9
|
||||||
|
msgid "Analytics"
|
||||||
|
msgstr "分析"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/index.html:8
|
||||||
|
msgid "Log In"
|
||||||
|
msgstr "登入"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:9
|
||||||
|
msgid "Manage"
|
||||||
|
msgstr "管理"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:17
|
||||||
|
msgid ""
|
||||||
|
"This service hasn't collected any data yet. To get started, place the "
|
||||||
|
"following code snippet at the end of the <code><body></code> tag on "
|
||||||
|
"any page you'd like to track."
|
||||||
|
msgstr ""
|
||||||
|
"此服務尚未收集任何資料。要開始,請將以下程式碼片段放在您想要追蹤的任何頁面的 <code><body></code> 標籤的末端。"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:29
|
||||||
|
#: templates/dashboard/pages/service.html:158
|
||||||
|
#: templates/dashboard/pages/service.html:192
|
||||||
|
#: templates/dashboard/pages/service.html:226
|
||||||
|
#: templates/dashboard/pages/service.html:260
|
||||||
|
#: templates/dashboard/pages/service.html:295
|
||||||
|
msgid "sessions"
|
||||||
|
msgstr "工作階段"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:47
|
||||||
|
msgid "Load Time"
|
||||||
|
msgstr "載入時間"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:86
|
||||||
|
msgid "Hits/Session"
|
||||||
|
msgstr "每次工作階段點選次數"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:109
|
||||||
|
#: templates/dashboard/pages/service_session.html:51
|
||||||
|
msgid "Location"
|
||||||
|
msgstr "位置"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:133
|
||||||
|
msgid "No data yet..."
|
||||||
|
msgstr "尚無資料..."
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:141
|
||||||
|
msgid "Sessions by Geography"
|
||||||
|
msgstr "工作階段依地理位置"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:143
|
||||||
|
msgid "view table"
|
||||||
|
msgstr "檢視表格"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:153
|
||||||
|
#: templates/dashboard/pages/service_session.html:47
|
||||||
|
msgid "Country"
|
||||||
|
msgstr "國家"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:191
|
||||||
|
msgid "Referrer"
|
||||||
|
msgstr "參照來源"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:225
|
||||||
|
msgid "Operating System"
|
||||||
|
msgstr "作業系統"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:259
|
||||||
|
#: templates/dashboard/pages/service_session.html:27
|
||||||
|
msgid "Browser"
|
||||||
|
msgstr "瀏覽器"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:294
|
||||||
|
#: templates/dashboard/pages/service_session.html:35
|
||||||
|
msgid "Device Type"
|
||||||
|
msgstr "裝置類型"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service.html:329
|
||||||
|
msgid "View more sessions"
|
||||||
|
msgstr "檢視更多工作階段"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_create.html:5
|
||||||
|
#: templates/dashboard/pages/service_create.html:8
|
||||||
|
msgid "Create Service"
|
||||||
|
msgstr "建立服務"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_create.html:16
|
||||||
|
msgid "Create"
|
||||||
|
msgstr "建立"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_create.html:17
|
||||||
|
#: templates/dashboard/pages/service_update.html:30
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "取消"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_delete.html:5
|
||||||
|
#: templates/dashboard/pages/service_update.html:33
|
||||||
|
msgid "Delete"
|
||||||
|
msgstr "刪除"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_delete.html:12
|
||||||
|
msgid ""
|
||||||
|
"Are you sure you want to delete this service? All of its analytics and "
|
||||||
|
"associated data will be permanently deleted."
|
||||||
|
msgstr ""
|
||||||
|
"您確定要刪除此服務嗎?其所有分析和相關資料將被永久刪除。"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_session.html:31
|
||||||
|
msgid "Device"
|
||||||
|
msgstr "裝置"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_session.html:39
|
||||||
|
msgid "OS"
|
||||||
|
msgstr "作業系統"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_session.html:54
|
||||||
|
msgid "Open in Maps"
|
||||||
|
msgstr "在地圖中開啟"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_session.html:56
|
||||||
|
msgid "Unknown"
|
||||||
|
msgstr "未知"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_session.html:85
|
||||||
|
msgid "Load"
|
||||||
|
msgstr "載入"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_session.html:89
|
||||||
|
msgid "Tracker"
|
||||||
|
msgstr "追蹤器"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_update.html:5
|
||||||
|
msgid "Management"
|
||||||
|
msgstr "管理"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_update.html:8
|
||||||
|
msgid "View"
|
||||||
|
msgstr "檢視"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_update.html:13
|
||||||
|
msgid "Installation"
|
||||||
|
msgstr "安裝"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_update.html:15
|
||||||
|
msgid ""
|
||||||
|
"Place the following snippet at the end of the <code><body></code> tag "
|
||||||
|
"on any page you'd like to track."
|
||||||
|
msgstr ""
|
||||||
|
"將以下片段放在您想要追蹤的任何頁面的 <code><body></code> 標籤的末端。"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_update.html:21
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr "設定"
|
||||||
|
|
||||||
|
#: templates/dashboard/pages/service_update.html:29
|
||||||
|
msgid "Save"
|
||||||
|
msgstr "儲存"
|
@ -24,7 +24,8 @@
|
|||||||
<script>
|
<script>
|
||||||
function getLocaleDateString(locale) {
|
function getLocaleDateString(locale) {
|
||||||
const formats = {
|
const formats = {
|
||||||
'de': "DD. MMM YY"
|
'de': "DD. MMM YY",
|
||||||
|
'zh_TW': "YYYY 年 MM 月 DD 日"
|
||||||
}
|
}
|
||||||
return formats[locale] || "MMM DD 'YY";
|
return formats[locale] || "MMM DD 'YY";
|
||||||
}
|
}
|
||||||
|
@ -168,6 +168,7 @@ def iconify(text):
|
|||||||
"firefox mobile": "firefox.com",
|
"firefox mobile": "firefox.com",
|
||||||
"edge mobile": "microsoft.com",
|
"edge mobile": "microsoft.com",
|
||||||
"chromium": "chromium.org",
|
"chromium": "chromium.org",
|
||||||
|
"duckduckgo mobile": "duckduckgo.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
domain = None
|
domain = None
|
||||||
|
@ -23,7 +23,7 @@ from django.contrib.messages import constants as messages
|
|||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
# Increment on new releases
|
# Increment on new releases
|
||||||
VERSION = "0.13.0"
|
VERSION = "0.13.1"
|
||||||
|
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user