Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06a7cbb0f1 | ||
|
|
24e0ff0129 | ||
|
|
45fafc3507 | ||
|
|
9e0c92d703 | ||
|
|
03f8cbfe7b | ||
|
|
120ea02fde | ||
|
|
cc16271683 | ||
|
|
78a47e3a74 | ||
|
|
b4c2ebc0bb | ||
|
|
55112fac88 | ||
|
|
4f2bf7a64e | ||
|
|
dfcbea56b8 | ||
|
|
f87c2c9d50 | ||
|
|
4cc2dd4b54 | ||
|
|
f1a0de2090 |
44
.github/workflows/build-docker-edge.yml
vendored
44
.github/workflows/build-docker-edge.yml
vendored
@@ -9,40 +9,50 @@ jobs:
|
||||
publish_to_docker_hub:
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 5
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare tags
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE=milesmcc/shynet
|
||||
TAGS="${DOCKER_IMAGE}:edge"
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
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
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
|
||||
44
.github/workflows/build-docker-manual.yml
vendored
44
.github/workflows/build-docker-manual.yml
vendored
@@ -9,40 +9,50 @@ jobs:
|
||||
publish_to_docker_hub:
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 5
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare tags
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE=milesmcc/shynet
|
||||
TAGS="${DOCKER_IMAGE}:${{ github.event.inputs.tag }}"
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
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
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
|
||||
43
.github/workflows/build-docker-release.yml
vendored
43
.github/workflows/build-docker-release.yml
vendored
@@ -9,41 +9,52 @@ jobs:
|
||||
publish_to_docker_hub:
|
||||
runs-on: ubuntu-latest
|
||||
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
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 5
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- 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}
|
||||
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||
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
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
tags: ${{ steps.metadata.outputs.tags }}
|
||||
labels: ${{ steps.metadata.outputs.labels }}
|
||||
33
Dockerfile
33
Dockerfile
@@ -7,37 +7,34 @@ WORKDIR /usr/src/shynet
|
||||
ARG GF_UID="500"
|
||||
ARG GF_GID="500"
|
||||
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,
|
||||
# which we indirectly rely on. Necessary for aarch64 support.
|
||||
|
||||
# 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 && \
|
||||
curl -m 180 "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&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-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 && \
|
||||
apk del curl
|
||||
apk --purge del curl
|
||||
|
||||
# Move dependency files
|
||||
COPY poetry.lock pyproject.toml ./
|
||||
COPY package.json package-lock.json ../
|
||||
# Django expects node_modules to be in its parent directory.
|
||||
|
||||
# Install more dependencies
|
||||
RUN apk add --no-cache postgresql-libs && \
|
||||
apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev libressl-dev libffi-dev && \
|
||||
# Install more dependencies and cleanup build dependencies afterwards
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev postgresql-dev libressl-dev libffi-dev && \
|
||||
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
|
||||
RUN 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
|
||||
|
||||
# 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 && \
|
||||
# Setup user group
|
||||
RUN addgroup --system -g $GF_GID appgroup && \
|
||||
adduser appuser --system --uid $GF_UID -G appgroup && \
|
||||
mkdir -p /var/local/shynet/db/ && \
|
||||
chown -R appuser:appgroup /var/local/shynet
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -673,9 +673,9 @@
|
||||
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -1243,9 +1243,9 @@
|
||||
"integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow=="
|
||||
},
|
||||
"word-wrap": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz",
|
||||
"integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA=="
|
||||
},
|
||||
"wordwrap": {
|
||||
"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]]
|
||||
name = "aiohttp"
|
||||
version = "3.8.1"
|
||||
version = "3.8.5"
|
||||
description = "Async http client/server framework (asyncio)"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
|
||||
{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.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.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
|
||||
{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.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.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
|
||||
{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.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.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
|
||||
{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.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.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
|
||||
{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.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.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
|
||||
{file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a94159871304770da4dd371f4291b20cac04e8c94f11bdea1c3478e557fbe0d8"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13bf85afc99ce6f9ee3567b04501f18f9f8dbbb2ea11ed1a2e079670403a7c84"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ce2ac5708501afc4847221a521f7e4b245abf5178cf5ddae9d5b3856ddb2f3a"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96943e5dcc37a6529d18766597c491798b7eb7a61d48878611298afc1fca946c"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ad5c3c4590bb3cc28b4382f031f3783f25ec223557124c68754a2231d989e2b"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c413c633d0512df4dc7fd2373ec06cc6a815b7b6d6c2f208ada7e9e93a5061d"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df72ac063b97837a80d80dec8d54c241af059cc9bb42c4de68bd5b61ceb37caa"},
|
||||
{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.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:368a42363c4d70ab52c2c6420a57f190ed3dfaca6a1b19afda8165ee16416a82"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7607ec3ce4993464368505888af5beb446845a014bc676d349efec0e05085905"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0d21c684808288a98914e5aaf2a7c6a3179d4df11d249799c32d1808e79503b5"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:312fcfbacc7880a8da0ae8b6abc6cc7d752e9caa0051a53d217a650b25e9a691"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad093e823df03bb3fd37e7dec9d4670c34f9e24aeace76808fc20a507cace825"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-win32.whl", hash = "sha256:33279701c04351a2914e1100b62b2a7fdb9a25995c4a104259f9a5ead7ed4802"},
|
||||
{file = "aiohttp-3.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:6e4a280e4b975a2e7745573e3fc9c9ba0d1194a3738ce1cbaa80626cc9b4f4df"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae871a964e1987a943d83d6709d20ec6103ca1eaf52f7e0d36ee1b5bebb8b9b9"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:461908b2578955045efde733719d62f2b649c404189a09a632d245b445c9c975"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72a860c215e26192379f57cae5ab12b168b75db8271f111019509a1196dfc780"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc14be025665dba6202b6a71cfcdb53210cc498e50068bc088076624471f8bb9"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af740fc2711ad85f1a5c034a435782fbd5b5f8314c9a3ef071424a8158d7f6b"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:841cd8233cbd2111a0ef0a522ce016357c5e3aff8a8ce92bcfa14cef890d698f"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed1c46fb119f1b59304b5ec89f834f07124cd23ae5b74288e364477641060ff"},
|
||||
{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.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62360cb771707cb70a6fd114b9871d20d7dd2163a0feafe43fd115cfe4fe845e"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23fb25a9f0a1ca1f24c0a371523546366bb642397c94ab45ad3aedf2941cec6a"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0ba0d15164eae3d878260d4c4df859bbdc6466e9e6689c344a13334f988bb53"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5d20003b635fc6ae3f96d7260281dfaf1894fc3aa24d1888a9b2628e97c241e5"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0175d745d9e85c40dcc51c8f88c74bfbaef9e7afeeeb9d03c37977270303064c"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-win32.whl", hash = "sha256:2e1b1e51b0774408f091d268648e3d57f7260c1682e7d3a63cb00d22d71bb945"},
|
||||
{file = "aiohttp-3.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:043d2299f6dfdc92f0ac5e995dfc56668e1587cea7f9aa9d8a78a1b6554e5755"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cae533195e8122584ec87531d6df000ad07737eaa3c81209e85c928854d2195c"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f21e83f355643c345177a5d1d8079f9f28b5133bcd154193b799d380331d5d3"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a75ef35f2df54ad55dbf4b73fe1da96f370e51b10c91f08b19603c64004acc"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e2e9839e14dd5308ee773c97115f1e0a1cb1d75cbeeee9f33824fa5144c7634"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44e65da1de4403d0576473e2344828ef9c4c6244d65cf4b75549bb46d40b8dd"},
|
||||
{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.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c7a815258e5895d8900aec4454f38dca9aed71085f227537208057853f9d13f2"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8b929b9bd7cd7c3939f8bcfffa92fae7480bd1aa425279d51a89327d600c704d"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5db3a5b833764280ed7618393832e0853e40f3d3e9aa128ac0ba0f8278d08649"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:a0215ce6041d501f3155dc219712bc41252d0ab76474615b9700d63d4d9292af"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fd1ed388ea7fbed22c4968dd64bab0198de60750a25fe8c0c9d4bef5abe13824"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-win32.whl", hash = "sha256:6e6783bcc45f397fdebc118d772103d751b54cddf5b60fbcc958382d7dd64f3e"},
|
||||
{file = "aiohttp-3.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:b5411d82cddd212644cf9360879eb5080f0d5f7d809d03262c50dad02f01421a"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:01d4c0c874aa4ddfb8098e85d10b5e875a70adc63db91f1ae65a4b04d3344cda"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5980a746d547a6ba173fd5ee85ce9077e72d118758db05d229044b469d9029a"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a482e6da906d5e6e653be079b29bc173a48e381600161c9932d89dfae5942ef"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80bd372b8d0715c66c974cf57fe363621a02f359f1ec81cba97366948c7fc873"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1161b345c0a444ebcf46bf0a740ba5dcf50612fd3d0528883fdc0eff578006a"},
|
||||
{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.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:153c2549f6c004d2754cc60603d4668899c9895b8a89397444a9c4efa282aaf4"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4a01951fabc4ce26ab791da5f3f24dca6d9a6f24121746eb19756416ff2d881b"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bfb9162dcf01f615462b995a516ba03e769de0789de1cadc0f916265c257e5d8"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7dde0009408969a43b04c16cbbe252c4f5ef4574ac226bc8815cd7342d2028b6"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4149d34c32f9638f38f544b3977a4c24052042affa895352d3636fa8bffd030a"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-win32.whl", hash = "sha256:68c5a82c8779bdfc6367c967a4a1b2aa52cd3595388bf5961a62158ee8a59e22"},
|
||||
{file = "aiohttp-3.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2cf57fb50be5f52bda004b8893e63b48530ed9f0d6c96c84620dc92fe3cd9b9d"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eca4bf3734c541dc4f374ad6010a68ff6c6748f00451707f39857f429ca36ced"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1274477e4c71ce8cfe6c1ec2f806d57c015ebf84d83373676036e256bc55d690"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c543e54710d6158fc6f439296c7865b29e0b616629767e685a7185fab4a6b9"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910bec0c49637d213f5d9877105d26e0c4a4de2f8b1b29405ff37e9fc0ad52b8"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5443910d662db951b2e58eb70b0fbe6b6e2ae613477129a5805d0b66c54b6cb7"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e460be6978fc24e3df83193dc0cc4de46c9909ed92dd47d349a452ef49325b7"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb1558def481d84f03b45888473fc5a1f35747b5f334ef4e7a571bc0dfcb11f8"},
|
||||
{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.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa1990247f02a54185dc0dff92a6904521172a22664c863a03ff64c42f9b5410"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0e584a10f204a617d71d359fe383406305a4b595b333721fa50b867b4a0a1548"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a3cf433f127efa43fee6b90ea4c6edf6c4a17109d1d037d1a52abec84d8f2e42"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c11f5b099adafb18e65c2c997d57108b5bbeaa9eeee64a84302c0978b1ec948b"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:84de26ddf621d7ac4c975dbea4c945860e08cccde492269db4e1538a6a6f3c35"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-win32.whl", hash = "sha256:ab88bafedc57dd0aab55fa728ea10c1911f7e4d8b43e1d838a1739f33712921c"},
|
||||
{file = "aiohttp-3.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:5798a9aad1879f626589f3df0f8b79b3608a92e9beab10e5fda02c8a2c60db2e"},
|
||||
{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]
|
||||
aiosignal = ">=1.1.2"
|
||||
async-timeout = ">=4.0.0a3,<5.0"
|
||||
attrs = ">=17.3.0"
|
||||
charset-normalizer = ">=2.0,<3.0"
|
||||
charset-normalizer = ">=2.0,<4.0"
|
||||
frozenlist = ">=1.1.1"
|
||||
multidict = ">=4.5,<7.0"
|
||||
yarl = ">=1.0,<2.0"
|
||||
@@ -258,13 +273,13 @@ zstd = ["zstandard"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2022.12.7"
|
||||
version = "2023.7.22"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"},
|
||||
{file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"},
|
||||
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
|
||||
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -493,30 +508,34 @@ toml = ["tomli"]
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "41.0.0"
|
||||
version = "41.0.4"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "cryptography-41.0.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-win32.whl", hash = "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928"},
|
||||
{file = "cryptography-41.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be"},
|
||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5"},
|
||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb"},
|
||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be"},
|
||||
{file = "cryptography-41.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9"},
|
||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2"},
|
||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d"},
|
||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895"},
|
||||
{file = "cryptography-41.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55"},
|
||||
{file = "cryptography-41.0.0.tar.gz", hash = "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-win32.whl", hash = "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd"},
|
||||
{file = "cryptography-41.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d"},
|
||||
{file = "cryptography-41.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67"},
|
||||
{file = "cryptography-41.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e"},
|
||||
{file = "cryptography-41.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829"},
|
||||
{file = "cryptography-41.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca"},
|
||||
{file = "cryptography-41.0.4-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d"},
|
||||
{file = "cryptography-41.0.4-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac"},
|
||||
{file = "cryptography-41.0.4-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9"},
|
||||
{file = "cryptography-41.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f"},
|
||||
{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]
|
||||
@@ -545,13 +564,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
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."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "Django-4.1.10-py3-none-any.whl", hash = "sha256:26d0260c2fb8121009e62ffc548b2398dea2522b6454208a852fb0ef264c206c"},
|
||||
{file = "Django-4.1.10.tar.gz", hash = "sha256:56343019a9fd839e2e5bf203daf45f25af79d5bffa4c71d56eae4f4404d82ade"},
|
||||
{file = "Django-4.1.13-py3-none-any.whl", hash = "sha256:04ab3f6f46d084a0bba5a2c9a93a3a2eb3fe81589512367a75f79ee8acf790ce"},
|
||||
{file = "Django-4.1.13.tar.gz", hash = "sha256:94a3f471e833c8f124ee7a2de11e92f633991d975e3fa5bdd91e8abd66426318"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
||||
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 django.contrib.auth.models import AnonymousUser
|
||||
from http import HTTPStatus
|
||||
|
||||
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:
|
||||
@@ -11,13 +14,13 @@ class ApiTokenRequiredMixin:
|
||||
return AnonymousUser()
|
||||
|
||||
token = token.split(" ")[1]
|
||||
user = User.objects.filter(api_token=token).first()
|
||||
|
||||
return user if user else AnonymousUser()
|
||||
user: User = User.objects.filter(api_token=token).first()
|
||||
return user or AnonymousUser()
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
request.user = self._get_user_by_token(request)
|
||||
if not request.user.is_authenticated:
|
||||
return JsonResponse(data={}, status=403)
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
return (
|
||||
super().dispatch(request, *args, **kwargs)
|
||||
if request.user.is_authenticated
|
||||
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 django.http import JsonResponse
|
||||
from http import HTTPStatus
|
||||
|
||||
from django.db.models import Q
|
||||
from django.db.models.query import QuerySet
|
||||
from django.http import JsonResponse
|
||||
from django.views.generic import View
|
||||
|
||||
from dashboard.mixins import DateRangeMixin
|
||||
from core.models import Service
|
||||
|
||||
from core.utils import is_valid_uuid
|
||||
from dashboard.mixins import DateRangeMixin
|
||||
from .mixins import ApiTokenRequiredMixin
|
||||
|
||||
|
||||
def is_valid_uuid(value):
|
||||
try:
|
||||
uuid.UUID(value)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
class DashboardApiView(ApiTokenRequiredMixin, DateRangeMixin, View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
services = Service.objects.filter(
|
||||
Q(owner=request.user) | Q(collaborators__in=[request.user])
|
||||
).distinct()
|
||||
services = Service.objects.filter(Q(owner=request.user) | Q(collaborators__in=[request.user])).distinct()
|
||||
|
||||
uuid = request.GET.get("uuid")
|
||||
if uuid and is_valid_uuid(uuid):
|
||||
services = services.filter(uuid=uuid)
|
||||
uuid_ = request.GET.get("uuid")
|
||||
if uuid_ and is_valid_uuid(uuid_):
|
||||
services = services.filter(uuid=uuid_)
|
||||
|
||||
try:
|
||||
start = self.get_start_date()
|
||||
end = self.get_end_date()
|
||||
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 = [
|
||||
{
|
||||
"name": s.name,
|
||||
"uuid": s.uuid,
|
||||
"link": s.link,
|
||||
"stats": s.get_core_stats(start, end),
|
||||
"name": service.name,
|
||||
"uuid": service.uuid,
|
||||
"link": service.link,
|
||||
"stats": service.get_core_stats(start, end),
|
||||
}
|
||||
for s in services
|
||||
for service in services
|
||||
]
|
||||
|
||||
services_data = self._convert_querysets_to_lists(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 key, value in service_data["stats"].items():
|
||||
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>
|
||||
function getLocaleDateString(locale) {
|
||||
const formats = {
|
||||
'de': "DD. MMM YY"
|
||||
'de': "DD. MMM YY",
|
||||
'zh_TW': "YYYY 年 MM 月 DD 日"
|
||||
}
|
||||
return formats[locale] || "MMM DD 'YY";
|
||||
}
|
||||
|
||||
@@ -168,6 +168,7 @@ def iconify(text):
|
||||
"firefox mobile": "firefox.com",
|
||||
"edge mobile": "microsoft.com",
|
||||
"chromium": "chromium.org",
|
||||
"duckduckgo mobile": "duckduckgo.com",
|
||||
}
|
||||
|
||||
domain = None
|
||||
|
||||
Reference in New Issue
Block a user