Compare commits
45 Commits
v3.1.0-bet
...
v3.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b39a2b856 | ||
|
|
6d17ce699a | ||
|
|
20dbb35d44 | ||
|
|
36d9dd7354 | ||
|
|
5129f9bff8 | ||
|
|
496a56676d | ||
|
|
57e25524c7 | ||
|
|
614a9b468a | ||
|
|
94a5359080 | ||
|
|
38c5cd7b32 | ||
|
|
c664be5cc5 | ||
|
|
bafcb9a867 | ||
|
|
d322c13791 | ||
|
|
8e84e59c2f | ||
|
|
bd7e160e10 | ||
|
|
df849d5a5c | ||
|
|
5cf4e208c6 | ||
|
|
07ddd4f917 | ||
|
|
98abe514e1 | ||
|
|
e43bd651b6 | ||
|
|
6dc461c11e | ||
|
|
2ed90dfa34 | ||
|
|
14ce8ecf98 | ||
|
|
46526b564e | ||
|
|
d82e959734 | ||
|
|
187e46eae5 | ||
|
|
fe5f26aea5 | ||
|
|
69a7972cd6 | ||
|
|
354668b016 | ||
|
|
c7000951fe | ||
|
|
33ea6c17d2 | ||
|
|
90801b77fa | ||
|
|
4076a7e464 | ||
|
|
fd32e737a3 | ||
|
|
3ccc831a1f | ||
|
|
f3471880ee | ||
|
|
52f189563b | ||
|
|
7e39cb0dfe | ||
|
|
be836c296c | ||
|
|
6f6b1f4862 | ||
|
|
ada3531565 | ||
|
|
939ed26fd0 | ||
|
|
da0641c115 | ||
|
|
753b95baff | ||
|
|
9dd9829058 |
32
.env.example
Normal file
@@ -0,0 +1,32 @@
|
||||
PORT=3000
|
||||
ADDRESS=0.0.0.0
|
||||
SECRET=app_secret
|
||||
SECRET_FILE=app_secret_file
|
||||
APP_URL=http://localhost:3000
|
||||
USERS=your_user_password_hash
|
||||
USERS_FILE=users_file
|
||||
COOKIE_SECURE=false
|
||||
GITHUB_CLIENT_ID=github_client_id
|
||||
GITHUB_CLIENT_SECRET=github_client_secret
|
||||
GITHUB_CLIENT_SECRET_FILE=github_client_secret_file
|
||||
GOOGLE_CLIENT_ID=google_client_id
|
||||
GOOGLE_CLIENT_SECRET=google_client_secret
|
||||
GOOGLE_CLIENT_SECRET_FILE=google_client_secret_file
|
||||
TAILSCALE_CLIENT_ID=tailscale_client_id
|
||||
TAILSCALE_CLIENT_SECRET=tailscale_client_secret
|
||||
TAILSCALE_CLIENT_SECRET_FILE=tailscale__client_secret_file
|
||||
GENERIC_CLIENT_ID=generic_client_id
|
||||
GENERIC_CLIENT_SECRET=generic_client_secret
|
||||
GENERIC_CLIENT_SECRET_FILE=generic_client_secret_file
|
||||
GENERIC_SCOPES=generic_scopes
|
||||
GENERIC_AUTH_URL=generic_auth_url
|
||||
GENERIC_TOKEN_URL=generic_token_url
|
||||
GENERIC_USER_URL=generic_user_url
|
||||
DISABLE_CONTINUE=false
|
||||
OAUTH_WHITELIST=
|
||||
GENERIC_NAME=My OAuth
|
||||
SESSION_EXPIRY=7200
|
||||
LOGIN_TIMEOUT=300
|
||||
LOGIN_MAX_RETRIES=5
|
||||
LOG_LEVEL=0
|
||||
APP_TITLE=Tinyauth SSO
|
||||
6
.github/workflows/ci.yml
vendored
@@ -26,17 +26,17 @@ jobs:
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
cd site
|
||||
cd frontend
|
||||
bun install
|
||||
|
||||
- name: Build frontend
|
||||
run: |
|
||||
cd site
|
||||
cd frontend
|
||||
bun run build
|
||||
|
||||
- name: Copy frontend
|
||||
run: |
|
||||
cp -r site/dist internal/assets/dist
|
||||
cp -r frontend/dist internal/assets/dist
|
||||
|
||||
- name: Run tests
|
||||
run: go test -v ./...
|
||||
|
||||
111
.github/workflows/release.yml
vendored
@@ -6,7 +6,85 @@ on:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
binary-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.23.2"
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
cd frontend
|
||||
bun install
|
||||
|
||||
- name: Install backend dependencies
|
||||
run: |
|
||||
go mod tidy
|
||||
|
||||
- name: Build frontend
|
||||
run: |
|
||||
cd frontend
|
||||
bun run build
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cp -r frontend/dist internal/assets/dist
|
||||
CGO_ENABLED=0 go build -ldflags "-s -w" -o tinyauth-amd64
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tinyauth-amd64
|
||||
path: tinyauth-amd64
|
||||
|
||||
binary-build-arm:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "^1.23.2"
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: |
|
||||
cd frontend
|
||||
bun install
|
||||
|
||||
- name: Install backend dependencies
|
||||
run: |
|
||||
go mod tidy
|
||||
|
||||
- name: Build frontend
|
||||
run: |
|
||||
cd frontend
|
||||
bun run build
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cp -r frontend/dist internal/assets/dist
|
||||
CGO_ENABLED=0 go build -ldflags "-s -w" -o tinyauth-arm64
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tinyauth-arm64
|
||||
path: tinyauth-arm64
|
||||
|
||||
image-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -51,7 +129,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
build-arm:
|
||||
image-build-arm:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -96,11 +174,11 @@ jobs:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
image-merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
- build-arm
|
||||
- image-build
|
||||
- image-build-arm
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -125,12 +203,29 @@ jobs:
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository_owner }}/tinyauth
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}},prefix=v
|
||||
type=semver,pattern={{major}},prefix=v
|
||||
type=semver,pattern={{major}}.{{minor}},prefix=v
|
||||
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf 'ghcr.io/${{ github.repository_owner }}/tinyauth@sha256:%s ' *)
|
||||
|
||||
update-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- binary-build
|
||||
- binary-build-arm
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: tinyauth-*
|
||||
path: binaries
|
||||
merge-multiple: true
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: binaries/*
|
||||
|
||||
98
.github/workflows/translations.yml
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
name: Publish translations
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- i18n_v*
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: pages
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
get-branches:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
i18n-branches: ${{ steps.get-branches.outputs.result }}
|
||||
steps:
|
||||
- name: Get branches
|
||||
id: get-branches
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { data: repos } = await github.rest.repos.listBranches({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
})
|
||||
|
||||
const i18nBranches = repos.filter((branch) => branch.name.startsWith("i18n_v"))
|
||||
const i18nBranchNames = i18nBranches.map((branch) => branch.name)
|
||||
|
||||
return i18nBranchNames
|
||||
|
||||
get-translations:
|
||||
needs: get-branches
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
branch: ${{ fromJson(needs.get-branches.outputs.i18n-branches) }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ matrix.branch }}
|
||||
|
||||
- name: Get translation version
|
||||
id: get-version
|
||||
run: |
|
||||
branch=${{ matrix.branch }}
|
||||
version=${branch#i18n_}
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.get-version.outputs.version }}
|
||||
path: frontend/src/lib/i18n/locales
|
||||
|
||||
build:
|
||||
needs: get-translations
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
- name: Prepare output directory
|
||||
run: |
|
||||
mkdir -p dist/i18n/
|
||||
|
||||
- name: Download translations
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist/i18n/
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: dist
|
||||
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
8
.gitignore
vendored
@@ -18,4 +18,10 @@ secret_oauth.txt
|
||||
.vscode
|
||||
|
||||
# apple stuff
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
# env
|
||||
.env
|
||||
|
||||
# tmp directory
|
||||
tmp
|
||||
@@ -1,6 +1,6 @@
|
||||
# Contributing
|
||||
|
||||
Contributing is relatively easy.
|
||||
Contributing is relatively easy, you just need to follow the steps carefully and you will be up and running with a development server in less than 5 minutes.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -20,62 +20,37 @@ cd tinyauth
|
||||
|
||||
## Install requirements
|
||||
|
||||
Now it's time to install the requirements, firstly the Go ones:
|
||||
Although you will not need the requirements in your machine since the development will happen in docker, I still recommend to install them because this way you will not have import errors, to install the go requirements, run:
|
||||
|
||||
```sh
|
||||
go mod download
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
And now the site ones:
|
||||
You also need to download the frontend dependencies, this can be done like so:
|
||||
|
||||
```sh
|
||||
cd site
|
||||
bun i
|
||||
cd frontend/
|
||||
bun install
|
||||
```
|
||||
|
||||
## Developing locally
|
||||
## Create your `.env` file
|
||||
|
||||
In order to develop the app locally you need to build the frontend and copy it to the assets folder in order for Go to embed it and host it. In order to build the frontend run:
|
||||
In order to configure the app you need to create an environment file, this can be done by copying the `.env.example` file to `.env` and modifying the environment variables inside to suit your needs.
|
||||
|
||||
```sh
|
||||
cd site
|
||||
bun run build
|
||||
cd ..
|
||||
```
|
||||
## Developing
|
||||
|
||||
Copy it to the assets folder:
|
||||
|
||||
```sh
|
||||
rm -rf internal/assets/dist
|
||||
cp -r site/dist internal/assets/dist
|
||||
```
|
||||
|
||||
Finally either run the app with:
|
||||
|
||||
```sh
|
||||
go run main.go
|
||||
```
|
||||
|
||||
Or build it with:
|
||||
|
||||
```sh
|
||||
go build
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Make sure you have set the environment variables when running outside of docker else the app will fail.
|
||||
|
||||
## Developing in docker
|
||||
|
||||
My recommended development method is docker so I can test that both my image works and that the app responds correctly to traefik. In my setup I have set these two DNS records in my DNS server:
|
||||
I have designed the development workflow to be entirely in docker, this is because it will directly work with traefik and you will not need to do any building in your host machine. The recommended development setup is to have a subdomain pointing to your machine like this:
|
||||
|
||||
```
|
||||
*.dev.local -> 127.0.0.1
|
||||
dev.local -> 127.0.0.1
|
||||
*.dev.example.com -> 127.0.0.1
|
||||
dev.example.com -> 127.0.0.1
|
||||
```
|
||||
|
||||
Then I can just make sure the domains are correct in the example docker compose file and do:
|
||||
Then you can just make sure the domains are correct in the example docker compose file and run:
|
||||
|
||||
```sh
|
||||
docker compose -f docker-compose.dev.yml up --build
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> I would recommend copying the example `docker-compose.dev.yml` into a `docker-compose.test.yml` file, so as you don't accidentally commit any sensitive information.
|
||||
|
||||
33
Dockerfile
@@ -1,22 +1,22 @@
|
||||
# Site builder
|
||||
FROM oven/bun:1.1.45-alpine AS site-builder
|
||||
FROM oven/bun:1.1.45-alpine AS frontend-builder
|
||||
|
||||
WORKDIR /site
|
||||
WORKDIR /frontend
|
||||
|
||||
COPY ./site/package.json ./
|
||||
COPY ./site/bun.lockb ./
|
||||
COPY ./frontend/package.json ./
|
||||
COPY ./frontend/bun.lockb ./
|
||||
|
||||
RUN bun install
|
||||
|
||||
COPY ./site/public ./public
|
||||
COPY ./site/src ./src
|
||||
COPY ./site/eslint.config.js ./
|
||||
COPY ./site/index.html ./
|
||||
COPY ./site/tsconfig.json ./
|
||||
COPY ./site/tsconfig.app.json ./
|
||||
COPY ./site/tsconfig.node.json ./
|
||||
COPY ./site/vite.config.ts ./
|
||||
COPY ./site/postcss.config.cjs ./
|
||||
COPY ./frontend/public ./public
|
||||
COPY ./frontend/src ./src
|
||||
COPY ./frontend/eslint.config.js ./
|
||||
COPY ./frontend/index.html ./
|
||||
COPY ./frontend/tsconfig.json ./
|
||||
COPY ./frontend/tsconfig.app.json ./
|
||||
COPY ./frontend/tsconfig.node.json ./
|
||||
COPY ./frontend/vite.config.ts ./
|
||||
COPY ./frontend/postcss.config.cjs ./
|
||||
|
||||
RUN bun run build
|
||||
|
||||
@@ -33,7 +33,7 @@ RUN go mod download
|
||||
COPY ./main.go ./
|
||||
COPY ./cmd ./cmd
|
||||
COPY ./internal ./internal
|
||||
COPY --from=site-builder /site/dist ./internal/assets/dist
|
||||
COPY --from=frontend-builder /frontend/dist ./internal/assets/dist
|
||||
|
||||
RUN CGO_ENABLED=0 go build -ldflags "-s -w"
|
||||
|
||||
@@ -42,8 +42,13 @@ FROM alpine:3.21 AS runner
|
||||
|
||||
WORKDIR /tinyauth
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
COPY --from=builder /tinyauth/tinyauth ./
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=5s \
|
||||
CMD curl -f http://localhost:3000/api/healthcheck || exit 1
|
||||
|
||||
ENTRYPOINT ["./tinyauth"]
|
||||
22
Dockerfile.dev
Normal file
@@ -0,0 +1,22 @@
|
||||
FROM golang:1.23-alpine3.21
|
||||
|
||||
WORKDIR /tinyauth
|
||||
|
||||
COPY go.mod ./
|
||||
COPY go.sum ./
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY ./cmd ./cmd
|
||||
COPY ./internal ./internal
|
||||
COPY ./main.go ./
|
||||
COPY ./air.toml ./
|
||||
|
||||
RUN mkdir -p ./internal/assets/dist && \
|
||||
echo "app running" > ./internal/assets/dist/index.html
|
||||
|
||||
RUN go install github.com/air-verse/air@v1.61.7
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENTRYPOINT ["air", "-c", "air.toml"]
|
||||
25
Makefile
@@ -1,25 +0,0 @@
|
||||
# Build website
|
||||
web:
|
||||
cd site; bun run build
|
||||
|
||||
# Copy site assets
|
||||
assets: web
|
||||
rm -rf internal/assets/dist
|
||||
mkdir -p internal/assets/dist
|
||||
cp -r site/dist/* internal/assets/dist
|
||||
|
||||
# Run development binary
|
||||
run: assets
|
||||
go run main.go
|
||||
|
||||
# Test
|
||||
test:
|
||||
go test ./...
|
||||
|
||||
# Build
|
||||
build: assets
|
||||
go build -o tinyauth
|
||||
|
||||
# Build no site
|
||||
build-skip-web:
|
||||
go build -o tinyauth
|
||||
17
README.md
@@ -1,5 +1,5 @@
|
||||
<div align="center">
|
||||
<img alt="Tinyauth" title="Tinyauth" width="256" src="site/public/logo.png">
|
||||
<img alt="Tinyauth" title="Tinyauth" width="256" src="frontend/public/logo.png">
|
||||
<h1>Tinyauth</h1>
|
||||
<p>The easiest way to secure your apps with a login screen.</p>
|
||||
</div>
|
||||
@@ -10,12 +10,15 @@
|
||||
<img alt="Commit activity" src="https://img.shields.io/github/commit-activity/w/steveiliop56/tinyauth">
|
||||
<img alt="Issues" src="https://img.shields.io/github/issues/steveiliop56/tinyauth">
|
||||
<img alt="Tinyauth CI" src="https://github.com/steveiliop56/tinyauth/actions/workflows/ci.yml/badge.svg">
|
||||
<a title="Crowdin" target="_blank" href="https://crowdin.com/project/tinyauth"><img src="https://badges.crowdin.net/tinyauth/localized.svg"></a>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps. It is made for traefik but it can be extended to work with all reverse proxies like caddy and nginx.
|
||||
|
||||

|
||||
|
||||
> [!WARNING]
|
||||
> Tinyauth is in active development and configuration may change often. Please make sure to carefully read the release notes before updating.
|
||||
|
||||
@@ -28,16 +31,20 @@ I just made a Discord server for Tinyauth! It is not only for Tinyauth but gener
|
||||
|
||||
## Getting Started
|
||||
|
||||
You can easily get started with tinyauth by following the guide on the [documentation](https://tinyauth.doesmycode.work/docs/getting-started.html). There is also an available [docker compose file](./docker-compose.example.yml) that has traefik, nginx and tinyauth to demonstrate its capabilities.
|
||||
You can easily get started with tinyauth by following the guide on the [documentation](https://tinyauth.app/docs/getting-started.html). There is also an available [docker compose file](./docker-compose.example.yml) that has traefik, nginx and tinyauth to demonstrate its capabilities.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find documentation and guides on all available configuration of tinyauth [here](https://tinyauth.doesmycode.work).
|
||||
You can find documentation and guides on all available configuration of tinyauth [here](https://tinyauth.app).
|
||||
|
||||
## Contributing
|
||||
|
||||
All contributions to the codebase are welcome! If you have any recommendations on how to improve security or find a security issue in tinyauth please open an issue or pull request so it can be fixed as soon as possible!
|
||||
|
||||
## Localization
|
||||
|
||||
If you would like to help translating the project in more languages you can do so by visiting the [Crowdin](https://crowdin.com/project/tinyauth) page.
|
||||
|
||||
## License
|
||||
|
||||
Tinyauth is licensed under the GNU General Public License v3.0. TL;DR — You may copy, distribute and modify the software as long as you track changes/dates in source files. Any modifications to or software including (via compiler) GPL-licensed code must also be made available under the GPL along with build & install instructions. For more information about the license check the [license](./LICENSE) file.
|
||||
@@ -56,3 +63,7 @@ Credits for the logo of this app go to:
|
||||
|
||||
- **Freepik** for providing the police hat and badge.
|
||||
- **Renee French** for the original gopher logo.
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#steveiliop56/tinyauth&Date)
|
||||
|
||||
23
air.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
root = "/tinyauth"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
cmd = "go build -o ./tmp/tinyauth ."
|
||||
bin = "tmp/tinyauth"
|
||||
include_ext = ["go"]
|
||||
exclude_dir = ["internal/assets/dist"]
|
||||
exclude_regex = [".*_test\\.go"]
|
||||
stop_on_error = true
|
||||
|
||||
[color]
|
||||
main = "magenta"
|
||||
watcher = "cyan"
|
||||
build = "yellow"
|
||||
runner = "green"
|
||||
|
||||
[misc]
|
||||
clean_on_exit = true
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
||||
@@ -3,8 +3,8 @@
|
||||
"embeds": [
|
||||
{
|
||||
"title": "Welcome to Tinyauth Discord!",
|
||||
"description": "Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps.\n\n**Information**\n\n• Github: <https://github.com/steveiliop56/tinyauth>\n• Website: <https://tinyauth.doesmycode.work>",
|
||||
"url": "https://tinyauth.doesmycode.work",
|
||||
"description": "Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps.\n\n**Information**\n\n• Github: <https://github.com/steveiliop56/tinyauth>\n• Website: <https://tinyauth.app>",
|
||||
"url": "https://tinyauth.app",
|
||||
"color": 7002085,
|
||||
"author": {
|
||||
"name": "Tinyauth"
|
||||
@@ -12,11 +12,11 @@
|
||||
"footer": {
|
||||
"text": "Updated at"
|
||||
},
|
||||
"timestamp": "2025-02-06T22:00:00.000Z",
|
||||
"timestamp": "2025-03-10T19:00:00.000Z",
|
||||
"thumbnail": {
|
||||
"url": "https://github.com/steveiliop56/tinyauth/blob/main/site/public/logo.png?raw=true"
|
||||
"url": "https://github.com/steveiliop56/tinyauth/blob/main/frontend/public/logo.png?raw=true"
|
||||
}
|
||||
}
|
||||
],
|
||||
"attachments": []
|
||||
}
|
||||
}
|
||||
BIN
assets/login.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
74
cmd/root.go
@@ -11,6 +11,7 @@ import (
|
||||
"tinyauth/internal/assets"
|
||||
"tinyauth/internal/auth"
|
||||
"tinyauth/internal/docker"
|
||||
"tinyauth/internal/handlers"
|
||||
"tinyauth/internal/hooks"
|
||||
"tinyauth/internal/providers"
|
||||
"tinyauth/internal/types"
|
||||
@@ -33,8 +34,8 @@ var rootCmd = &cobra.Command{
|
||||
|
||||
// Get config
|
||||
var config types.Config
|
||||
parseErr := viper.Unmarshal(&config)
|
||||
HandleError(parseErr, "Failed to parse config")
|
||||
err := viper.Unmarshal(&config)
|
||||
HandleError(err, "Failed to parse config")
|
||||
|
||||
// Secrets
|
||||
config.Secret = utils.GetSecret(config.Secret, config.SecretFile)
|
||||
@@ -45,8 +46,8 @@ var rootCmd = &cobra.Command{
|
||||
|
||||
// Validate config
|
||||
validator := validator.New()
|
||||
validateErr := validator.Struct(config)
|
||||
HandleError(validateErr, "Failed to validate config")
|
||||
err = validator.Struct(config)
|
||||
HandleError(err, "Failed to validate config")
|
||||
|
||||
// Logger
|
||||
log.Logger = log.Level(zerolog.Level(config.LogLevel))
|
||||
@@ -54,9 +55,8 @@ var rootCmd = &cobra.Command{
|
||||
|
||||
// Users
|
||||
log.Info().Msg("Parsing users")
|
||||
users, usersErr := utils.GetUsers(config.Users, config.UsersFile)
|
||||
|
||||
HandleError(usersErr, "Failed to parse users")
|
||||
users, err := utils.GetUsers(config.Users, config.UsersFile)
|
||||
HandleError(err, "Failed to parse users")
|
||||
|
||||
if len(users) == 0 && !utils.OAuthConfigured(config) {
|
||||
HandleError(errors.New("no users or OAuth configured"), "No users or OAuth configured")
|
||||
@@ -66,8 +66,15 @@ var rootCmd = &cobra.Command{
|
||||
oauthWhitelist := utils.Filter(strings.Split(config.OAuthWhitelist, ","), func(val string) bool {
|
||||
return val != ""
|
||||
})
|
||||
|
||||
log.Debug().Msg("Parsed OAuth whitelist")
|
||||
|
||||
// Get domain
|
||||
log.Debug().Msg("Getting domain")
|
||||
domain, err := utils.GetUpperDomain(config.AppURL)
|
||||
HandleError(err, "Failed to get upper domain")
|
||||
log.Info().Str("domain", domain).Msg("Using domain for cookie store")
|
||||
|
||||
// Create OAuth config
|
||||
oauthConfig := types.OAuthConfig{
|
||||
GithubClientId: config.GithubClientId,
|
||||
@@ -85,17 +92,41 @@ var rootCmd = &cobra.Command{
|
||||
AppURL: config.AppURL,
|
||||
}
|
||||
|
||||
log.Debug().Msg("Parsed OAuth config")
|
||||
// Create handlers config
|
||||
handlersConfig := types.HandlersConfig{
|
||||
AppURL: config.AppURL,
|
||||
DisableContinue: config.DisableContinue,
|
||||
Title: config.Title,
|
||||
GenericName: config.GenericName,
|
||||
}
|
||||
|
||||
// Create api config
|
||||
apiConfig := types.APIConfig{
|
||||
Port: config.Port,
|
||||
Address: config.Address,
|
||||
}
|
||||
|
||||
// Create auth config
|
||||
authConfig := types.AuthConfig{
|
||||
Users: users,
|
||||
OauthWhitelist: oauthWhitelist,
|
||||
Secret: config.Secret,
|
||||
CookieSecure: config.CookieSecure,
|
||||
SessionExpiry: config.SessionExpiry,
|
||||
Domain: domain,
|
||||
LoginTimeout: config.LoginTimeout,
|
||||
LoginMaxRetries: config.LoginMaxRetries,
|
||||
}
|
||||
|
||||
// Create docker service
|
||||
docker := docker.NewDocker()
|
||||
|
||||
// Initialize docker
|
||||
dockerErr := docker.Init()
|
||||
HandleError(dockerErr, "Failed to initialize docker")
|
||||
err = docker.Init()
|
||||
HandleError(err, "Failed to initialize docker")
|
||||
|
||||
// Create auth service
|
||||
auth := auth.NewAuth(docker, users, oauthWhitelist, config.SessionExpiry)
|
||||
auth := auth.NewAuth(authConfig, docker)
|
||||
|
||||
// Create OAuth providers service
|
||||
providers := providers.NewProviders(oauthConfig)
|
||||
@@ -106,18 +137,11 @@ var rootCmd = &cobra.Command{
|
||||
// Create hooks service
|
||||
hooks := hooks.NewHooks(auth, providers)
|
||||
|
||||
// Create handlers
|
||||
handlers := handlers.NewHandlers(handlersConfig, auth, hooks, providers, docker)
|
||||
|
||||
// Create API
|
||||
api := api.NewAPI(types.APIConfig{
|
||||
Port: config.Port,
|
||||
Address: config.Address,
|
||||
Secret: config.Secret,
|
||||
AppURL: config.AppURL,
|
||||
CookieSecure: config.CookieSecure,
|
||||
DisableContinue: config.DisableContinue,
|
||||
SessionExpiry: config.SessionExpiry,
|
||||
Title: config.Title,
|
||||
GenericName: config.GenericName,
|
||||
}, hooks, auth, providers)
|
||||
api := api.NewAPI(apiConfig, handlers)
|
||||
|
||||
// Setup routes
|
||||
api.Init()
|
||||
@@ -134,7 +158,7 @@ func Execute() {
|
||||
}
|
||||
|
||||
func HandleError(err error, msg string) {
|
||||
// If error log it and exit
|
||||
// If error, log it and exit
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg(msg)
|
||||
}
|
||||
@@ -179,6 +203,8 @@ func init() {
|
||||
rootCmd.Flags().Bool("disable-continue", false, "Disable continue screen and redirect to app directly.")
|
||||
rootCmd.Flags().String("oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth.")
|
||||
rootCmd.Flags().Int("session-expiry", 86400, "Session (cookie) expiration time in seconds.")
|
||||
rootCmd.Flags().Int("login-timeout", 300, "Login timeout in seconds after max retries reached (0 to disable).")
|
||||
rootCmd.Flags().Int("login-max-retries", 5, "Maximum login attempts before timeout (0 to disable).")
|
||||
rootCmd.Flags().Int("log-level", 1, "Log level.")
|
||||
rootCmd.Flags().String("app-title", "Tinyauth", "Title of the app.")
|
||||
|
||||
@@ -213,6 +239,8 @@ func init() {
|
||||
viper.BindEnv("session-expiry", "SESSION_EXPIRY")
|
||||
viper.BindEnv("log-level", "LOG_LEVEL")
|
||||
viper.BindEnv("app-title", "APP_TITLE")
|
||||
viper.BindEnv("login-timeout", "LOGIN_TIMEOUT")
|
||||
viper.BindEnv("login-max-retries", "LOGIN_MAX_RETRIES")
|
||||
|
||||
// Bind flags to viper
|
||||
viper.BindPFlags(rootCmd.Flags())
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// Interactive flag
|
||||
var interactive bool
|
||||
|
||||
// i stands for input
|
||||
// Input user
|
||||
var iUser string
|
||||
|
||||
var GenerateCmd = &cobra.Command{
|
||||
@@ -46,18 +46,18 @@ var GenerateCmd = &cobra.Command{
|
||||
)
|
||||
|
||||
// Run form
|
||||
formErr := form.WithTheme(baseTheme).Run()
|
||||
err := form.WithTheme(baseTheme).Run()
|
||||
|
||||
if formErr != nil {
|
||||
log.Fatal().Err(formErr).Msg("Form failed")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Form failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Parse user
|
||||
user, parseErr := utils.ParseUser(iUser)
|
||||
user, err := utils.ParseUser(iUser)
|
||||
|
||||
if parseErr != nil {
|
||||
log.Fatal().Err(parseErr).Msg("Failed to parse user")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to parse user")
|
||||
}
|
||||
|
||||
// Check if user was using docker escape
|
||||
@@ -73,13 +73,13 @@ var GenerateCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// Generate totp secret
|
||||
key, keyErr := totp.Generate(totp.GenerateOpts{
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: "Tinyauth",
|
||||
AccountName: user.Username,
|
||||
})
|
||||
|
||||
if keyErr != nil {
|
||||
log.Fatal().Err(keyErr).Msg("Failed to generate totp secret")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to generate totp secret")
|
||||
}
|
||||
|
||||
// Create secret
|
||||
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Interactive flag
|
||||
var interactive bool
|
||||
|
||||
// Docker flag
|
||||
var docker bool
|
||||
|
||||
// i stands for input
|
||||
@@ -51,10 +54,10 @@ var CreateCmd = &cobra.Command{
|
||||
// Use simple theme
|
||||
var baseTheme *huh.Theme = huh.ThemeBase()
|
||||
|
||||
formErr := form.WithTheme(baseTheme).Run()
|
||||
err := form.WithTheme(baseTheme).Run()
|
||||
|
||||
if formErr != nil {
|
||||
log.Fatal().Err(formErr).Msg("Form failed")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Form failed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,10 +69,10 @@ var CreateCmd = &cobra.Command{
|
||||
log.Info().Str("username", iUsername).Str("password", iPassword).Bool("docker", docker).Msg("Creating user")
|
||||
|
||||
// Hash password
|
||||
password, passwordErr := bcrypt.GenerateFromPassword([]byte(iPassword), bcrypt.DefaultCost)
|
||||
password, err := bcrypt.GenerateFromPassword([]byte(iPassword), bcrypt.DefaultCost)
|
||||
|
||||
if passwordErr != nil {
|
||||
log.Fatal().Err(passwordErr).Msg("Failed to hash password")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to hash password")
|
||||
}
|
||||
|
||||
// Convert password to string
|
||||
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Interactive flag
|
||||
var interactive bool
|
||||
|
||||
// Docker flag
|
||||
var docker bool
|
||||
|
||||
// i stands for input
|
||||
@@ -60,18 +63,18 @@ var VerifyCmd = &cobra.Command{
|
||||
)
|
||||
|
||||
// Run form
|
||||
formErr := form.WithTheme(baseTheme).Run()
|
||||
err := form.WithTheme(baseTheme).Run()
|
||||
|
||||
if formErr != nil {
|
||||
log.Fatal().Err(formErr).Msg("Form failed")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Form failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Parse user
|
||||
user, userErr := utils.ParseUser(iUser)
|
||||
user, err := utils.ParseUser(iUser)
|
||||
|
||||
if userErr != nil {
|
||||
log.Fatal().Err(userErr).Msg("Failed to parse user")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to parse user")
|
||||
}
|
||||
|
||||
// Compare username
|
||||
@@ -80,9 +83,9 @@ var VerifyCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// Compare password
|
||||
verifyErr := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(iPassword))
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(iPassword))
|
||||
|
||||
if verifyErr != nil {
|
||||
if err != nil {
|
||||
log.Fatal().Msg("Ppassword is incorrect")
|
||||
}
|
||||
|
||||
@@ -96,9 +99,9 @@ var VerifyCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// Check totp code
|
||||
totpOk := totp.Validate(iTotp, user.TotpSecret)
|
||||
ok := totp.Validate(iTotp, user.TotpSecret)
|
||||
|
||||
if !totpOk {
|
||||
if !ok {
|
||||
log.Fatal().Msg("Totp code incorrect")
|
||||
|
||||
}
|
||||
|
||||
12
crowdin.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
"base_path": "."
|
||||
"base_url": "https://api.crowdin.com"
|
||||
|
||||
"preserve_hierarchy": true
|
||||
|
||||
files:
|
||||
[
|
||||
{
|
||||
"source": "/frontend/src/lib/i18n/locales/en.json",
|
||||
"translation": "/frontend/src/lib/i18n/locales/%locale%.json",
|
||||
},
|
||||
]
|
||||
@@ -13,22 +13,35 @@ services:
|
||||
image: traefik/whoami:latest
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.nginx.rule: Host(`whoami.dev.local`)
|
||||
traefik.http.services.nginx.loadbalancer.server.port: 80
|
||||
traefik.http.routers.nginx.rule: Host(`whoami.example.com`)
|
||||
traefik.http.routers.nginx.middlewares: tinyauth
|
||||
|
||||
tinyauth:
|
||||
container_name: tinyauth
|
||||
tinyauth-frontend:
|
||||
container_name: tinyauth-frontend
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
- SECRET=some-random-32-chars-string
|
||||
- APP_URL=http://tinyauth.dev.local
|
||||
- USERS=user:$$2a$$10$$UdLYoJ5lgPsC0RKqYH/jMua7zIn0g9kPqWmhYayJYLaZQ/FTmH2/u # user:password
|
||||
dockerfile: frontend/Dockerfile.dev
|
||||
volumes:
|
||||
- ./frontend/src:/frontend/src
|
||||
ports:
|
||||
- 5173:5173
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.tinyauth.rule: Host(`tinyauth.dev.local`)
|
||||
traefik.http.services.tinyauth.loadbalancer.server.port: 3000
|
||||
traefik.http.middlewares.tinyauth.forwardauth.address: http://tinyauth:3000/api/auth/traefik
|
||||
traefik.http.middlewares.tinyauth.forwardauth.authResponseHeaders: Remote-User
|
||||
traefik.http.routers.tinyauth.rule: Host(`tinyauth.example.com`)
|
||||
|
||||
tinyauth-backend:
|
||||
container_name: tinyauth-backend
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./internal:/tinyauth/internal
|
||||
- ./cmd:/tinyauth/cmd
|
||||
- ./main.go:/tinyauth/main.go
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
ports:
|
||||
- 3000:3000
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.middlewares.tinyauth.forwardauth.address: http://tinyauth-backend:3000/api/auth/traefik
|
||||
|
||||
@@ -8,18 +8,17 @@ services:
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
nginx:
|
||||
container_name: nginx
|
||||
image: nginx:latest
|
||||
whoami:
|
||||
container_name: whoami
|
||||
image: traefik/whoami:latest
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.nginx.rule: Host(`nginx.example.com`)
|
||||
traefik.http.services.nginx.loadbalancer.server.port: 80
|
||||
traefik.http.routers.nginx.rule: Host(`whoami.example.com`)
|
||||
traefik.http.routers.nginx.middlewares: tinyauth
|
||||
|
||||
tinyauth:
|
||||
container_name: tinyauth
|
||||
image: ghcr.io/steveiliop56/tinyauth:latest
|
||||
image: ghcr.io/steveiliop56/tinyauth:v3
|
||||
environment:
|
||||
- SECRET=some-random-32-chars-string
|
||||
- APP_URL=https://tinyauth.example.com
|
||||
@@ -27,6 +26,4 @@ services:
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.http.routers.tinyauth.rule: Host(`tinyauth.example.com`)
|
||||
traefik.http.services.tinyauth.loadbalancer.server.port: 3000
|
||||
traefik.http.middlewares.tinyauth.forwardauth.address: http://tinyauth:3000/api/auth/traefik
|
||||
traefik.http.middlewares.tinyauth.forwardauth.authResponseHeaders: Remote-User
|
||||
|
||||
0
site/.gitignore → frontend/.gitignore
vendored
23
frontend/Dockerfile.dev
Normal file
@@ -0,0 +1,23 @@
|
||||
FROM oven/bun:1.1.45-alpine
|
||||
|
||||
WORKDIR /frontend
|
||||
|
||||
COPY ./frontend/package.json ./
|
||||
COPY ./frontend/bun.lockb ./
|
||||
|
||||
RUN bun install
|
||||
|
||||
COPY ./frontend/public ./public
|
||||
COPY ./frontend/src ./src
|
||||
|
||||
COPY ./frontend/eslint.config.js ./
|
||||
COPY ./frontend/index.html ./
|
||||
COPY ./frontend/tsconfig.json ./
|
||||
COPY ./frontend/tsconfig.app.json ./
|
||||
COPY ./frontend/tsconfig.node.json ./
|
||||
COPY ./frontend/vite.config.ts ./
|
||||
COPY ./frontend/postcss.config.cjs ./
|
||||
|
||||
EXPOSE 5173
|
||||
|
||||
ENTRYPOINT ["bun", "run", "dev"]
|
||||
BIN
frontend/bun.lockb
Executable file
@@ -6,7 +6,7 @@
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="manifest" href="/frontend.webmanifest" />
|
||||
<title>Tinyauth</title>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "site",
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "site",
|
||||
"name": "frontend",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@mantine/core": "^7.16.0",
|
||||
@@ -2246,4 +2246,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "site",
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
@@ -16,8 +16,14 @@
|
||||
"@mantine/notifications": "^7.16.0",
|
||||
"@tanstack/react-query": "4",
|
||||
"axios": "^1.7.9",
|
||||
"i18next": "^24.2.3",
|
||||
"i18next-browser-languagedetector": "^8.0.4",
|
||||
"i18next-chained-backend": "^4.6.2",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"i18next-resources-to-backend": "^1.2.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-router": "^7.1.3",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
@@ -38,4 +44,4 @@
|
||||
"typescript-eslint": "^8.18.2",
|
||||
"vite": "^6.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 602 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
@@ -1,6 +1,7 @@
|
||||
import { TextInput, PasswordInput, Button } from "@mantine/core";
|
||||
import { useForm, zodResolver } from "@mantine/form";
|
||||
import { LoginFormValues, loginSchema } from "../../schemas/login-schema";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface LoginFormProps {
|
||||
isLoading: boolean;
|
||||
@@ -9,6 +10,7 @@ interface LoginFormProps {
|
||||
|
||||
export const LoginForm = (props: LoginFormProps) => {
|
||||
const { isLoading, onSubmit } = props;
|
||||
const { t } = useTranslation();
|
||||
|
||||
const form = useForm({
|
||||
mode: "uncontrolled",
|
||||
@@ -22,15 +24,15 @@ export const LoginForm = (props: LoginFormProps) => {
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(onSubmit)}>
|
||||
<TextInput
|
||||
label="Username"
|
||||
placeholder="user@example.com"
|
||||
label={t("loginUsername")}
|
||||
placeholder="username"
|
||||
required
|
||||
disabled={isLoading}
|
||||
key={form.key("username")}
|
||||
{...form.getInputProps("username")}
|
||||
/>
|
||||
<PasswordInput
|
||||
label="Password"
|
||||
label={t("loginPassword")}
|
||||
placeholder="password"
|
||||
required
|
||||
mt="md"
|
||||
@@ -39,7 +41,7 @@ export const LoginForm = (props: LoginFormProps) => {
|
||||
{...form.getInputProps("password")}
|
||||
/>
|
||||
<Button fullWidth mt="xl" type="submit" loading={isLoading}>
|
||||
Login
|
||||
{t("loginSubmit")}
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
@@ -0,0 +1,40 @@
|
||||
import { ComboboxItem, Select } from "@mantine/core";
|
||||
import { useState } from "react";
|
||||
import i18n from "../../lib/i18n/i18n";
|
||||
import {
|
||||
SupportedLanguage,
|
||||
getLanguageName,
|
||||
languages,
|
||||
} from "../../lib/i18n/locales";
|
||||
|
||||
export const LanguageSelector = () => {
|
||||
const [language, setLanguage] = useState<ComboboxItem>({
|
||||
value: i18n.language,
|
||||
label: getLanguageName(i18n.language as SupportedLanguage),
|
||||
});
|
||||
|
||||
const languageOptions = Object.entries(languages).map(([code, name]) => ({
|
||||
value: code,
|
||||
label: name,
|
||||
}));
|
||||
|
||||
const handleLanguageChange = (option: string) => {
|
||||
i18n.changeLanguage(option as SupportedLanguage);
|
||||
setLanguage({
|
||||
value: option,
|
||||
label: getLanguageName(option as SupportedLanguage),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
data={languageOptions}
|
||||
value={language ? language.value : null}
|
||||
onChange={(_value, option) => handleLanguageChange(option.value)}
|
||||
allowDeselect={false}
|
||||
pos="absolute"
|
||||
right={10}
|
||||
top={10}
|
||||
/>
|
||||
);
|
||||
};
|
||||
16
frontend/src/components/layouts/layout.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Center, Flex } from "@mantine/core";
|
||||
import { ReactNode } from "react";
|
||||
import { LanguageSelector } from "../language-selector/language-selector";
|
||||
|
||||
export const Layout = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<>
|
||||
<LanguageSelector />
|
||||
<Center style={{ minHeight: "100vh" }}>
|
||||
<Flex direction="column" flex="1" maw={340}>
|
||||
{children}
|
||||
</Flex>
|
||||
</Center>
|
||||
</>
|
||||
);
|
||||
};
|
||||
42
frontend/src/context/app-context.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import React, { createContext, useContext } from "react";
|
||||
import axios from "axios";
|
||||
import { AppContextSchemaType } from "../schemas/app-context-schema";
|
||||
|
||||
const AppContext = createContext<AppContextSchemaType | null>(null);
|
||||
|
||||
export const AppContextProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const {
|
||||
data: userContext,
|
||||
isLoading,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ["appContext"],
|
||||
queryFn: async () => {
|
||||
const res = await axios.get("/api/app");
|
||||
return res.data;
|
||||
},
|
||||
});
|
||||
|
||||
if (error && !isLoading) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={userContext}>{children}</AppContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAppContext = () => {
|
||||
const context = useContext(AppContext);
|
||||
|
||||
if (context === null) {
|
||||
throw new Error("useAppContext must be used within an AppContextProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
};
|
||||
@@ -17,7 +17,7 @@ export const UserContextProvider = ({
|
||||
} = useQuery({
|
||||
queryKey: ["userContext"],
|
||||
queryFn: async () => {
|
||||
const res = await axios.get("/api/status");
|
||||
const res = await axios.get("/api/user");
|
||||
return res.data;
|
||||
},
|
||||
});
|
||||
27
frontend/src/icons/tailscale.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useMantineColorScheme } from "@mantine/core";
|
||||
import type { SVGProps } from "react";
|
||||
|
||||
export function TailscaleIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
width={24}
|
||||
height={24}
|
||||
{...props}
|
||||
>
|
||||
{colorScheme === "dark" ? (
|
||||
<>
|
||||
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 318.1c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9S1.8 219 1.8 254.2s28.6 63.9 63.8 63.9m191.6 0c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m0 193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m189.2-193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" fill="#ffffff"/>
|
||||
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 127.7c35.3 0 63.9-28.6 63.9-63.9S100.9 0 65.6 0 1.8 28.6 1.8 63.9s28.6 63.8 63.8 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.8 28.7-63.8 63.9S30.4 512 65.6 512m191.6-384.3c35.3 0 63.9-28.6 63.9-63.9S292.5 0 257.2 0s-63.9 28.6-63.9 63.9 28.6 63.8 63.9 63.8m189.2 0c35.3 0 63.9-28.6 63.9-63.9S481.6 0 446.4 0c-35.3 0-63.9 28.6-63.9 63.9s28.6 63.8 63.9 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" fill="#CCCAC9" opacity="0.2"/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 318.1c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9S1.8 219 1.8 254.2s28.6 63.9 63.8 63.9m191.6 0c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m0 193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m189.2-193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9"/>
|
||||
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 127.7c35.3 0 63.9-28.6 63.9-63.9S100.9 0 65.6 0 1.8 28.6 1.8 63.9s28.6 63.8 63.8 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.8 28.7-63.8 63.9S30.4 512 65.6 512m191.6-384.3c35.3 0 63.9-28.6 63.9-63.9S292.5 0 257.2 0s-63.9 28.6-63.9 63.9 28.6 63.8 63.9 63.8m189.2 0c35.3 0 63.9-28.6 63.9-63.9S481.6 0 446.4 0c-35.3 0-63.9 28.6-63.9 63.9s28.6 63.8 63.9 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" opacity=".2"/>
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
37
frontend/src/lib/i18n/i18n.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import ChainedBackend from "i18next-chained-backend";
|
||||
import resourcesToBackend from "i18next-resources-to-backend";
|
||||
import HttpBackend from "i18next-http-backend";
|
||||
|
||||
i18n
|
||||
.use(ChainedBackend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
fallbackLng: "en",
|
||||
debug: import.meta.env.MODE === "development",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
|
||||
load: "currentOnly",
|
||||
|
||||
backend: {
|
||||
backends: [
|
||||
HttpBackend,
|
||||
resourcesToBackend(
|
||||
(language: string) => import(`./locales/${language}.json`),
|
||||
),
|
||||
],
|
||||
backendOptions: [
|
||||
{
|
||||
loadPath: "https://cdn.tinyauth.app/i18n/v1/{{lng}}.json",
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
36
frontend/src/lib/i18n/locales.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export const languages = {
|
||||
"af-ZA": "Afrikaans",
|
||||
"ar-SA": "العربية",
|
||||
"ca-ES": "Català",
|
||||
"cs-CZ": "Čeština",
|
||||
"da-DK": "Dansk",
|
||||
"de-DE": "Deutsch",
|
||||
"el-GR": "Ελληνικά",
|
||||
"en-US": "English",
|
||||
"es-ES": "Español",
|
||||
"fi-FI": "Suomi",
|
||||
"fr-FR": "Français",
|
||||
"he-IL": "עברית",
|
||||
"hu-HU": "Magyar",
|
||||
"it-IT": "Italiano",
|
||||
"ja-JP": "日本語",
|
||||
"ko-KR": "한국어",
|
||||
"nl-NL": "Nederlands",
|
||||
"no-NO": "Norsk",
|
||||
"pl-PL": "Polski",
|
||||
"pt-BR": "Português",
|
||||
"pt-PT": "Português",
|
||||
"ro-RO": "Română",
|
||||
"ru-RU": "Русский",
|
||||
"sr-SP": "Српски",
|
||||
"sv-SE": "Svenska",
|
||||
"tr-TR": "Türkçe",
|
||||
"uk-UA": "Українська",
|
||||
"vi-VN": "Tiếng Việt",
|
||||
"zh-CN": "中文",
|
||||
"zh-TW": "中文"
|
||||
}
|
||||
|
||||
export type SupportedLanguage = keyof typeof languages;
|
||||
|
||||
export const getLanguageName = (language: SupportedLanguage): string => languages[language];
|
||||
46
frontend/src/lib/i18n/locales/af-ZA.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/ar-SA.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/ca-ES.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/cs-CZ.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/da-DK.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/de-DE.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Willkommen zurück, logge dich ein mit",
|
||||
"loginDivider": "Oder mit Passwort fortfahren",
|
||||
"loginUsername": "Benutzername",
|
||||
"loginPassword": "Passwort",
|
||||
"loginSubmit": "Anmelden",
|
||||
"loginFailTitle": "Login fehlgeschlagen",
|
||||
"loginFailSubtitle": "Bitte überprüfe deinen Benutzernamen und Passwort",
|
||||
"loginFailRateLimit": "Sie konnten sich zu oft nicht einloggen, bitte versuchen Sie es später erneut",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/el-GR.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Καλώς ήρθατε, συνδεθείτε με",
|
||||
"loginDivider": "Ή συνεχίστε με κωδικό πρόσβασης",
|
||||
"loginUsername": "Όνομα Χρήστη",
|
||||
"loginPassword": "Κωδικός",
|
||||
"loginSubmit": "Είσοδος",
|
||||
"loginFailTitle": "Αποτυχία σύνδεσης",
|
||||
"loginFailSubtitle": "Παρακαλώ ελέγξτε το όνομα χρήστη και τον κωδικό πρόσβασης",
|
||||
"loginFailRateLimit": "Αποτύχατε να συνδεθείτε πάρα πολλές φορές, παρακαλώ προσπαθήστε ξανά αργότερα",
|
||||
"loginSuccessTitle": "Συνδεδεμένος",
|
||||
"loginSuccessSubtitle": "Καλώς ήρθατε!",
|
||||
"loginOauthFailTitle": "Εσωτερικό σφάλμα",
|
||||
"loginOauthFailSubtitle": "Αποτυχία λήψης OAuth URL",
|
||||
"loginOauthSuccessTitle": "Ανακατεύθυνση",
|
||||
"loginOauthSuccessSubtitle": "Ανακατεύθυνση στον πάροχο OAuth σας",
|
||||
"continueRedirectingTitle": "Ανακατεύθυνση...",
|
||||
"continueRedirectingSubtitle": "Θα πρέπει να μεταφερθείτε σύντομα στην εφαρμογή σας",
|
||||
"continueInvalidRedirectTitle": "Μη έγκυρη ανακατεύθυνση",
|
||||
"continueInvalidRedirectSubtitle": "Το URL ανακατεύθυνσης δεν είναι έγκυρο",
|
||||
"continueInsecureRedirectTitle": "Μη ασφαλής ανακατεύθυνση",
|
||||
"continueInsecureRedirectSubtitle": "Προσπαθείτε να ανακατευθύνετε από <Code>https</Code> σε <Code>http</Code>, είστε σίγουροι ότι θέλετε να συνεχίσετε;",
|
||||
"continueTitle": "Συνέχεια",
|
||||
"continueSubtitle": "Κάντε κλικ στο κουμπί για να συνεχίσετε στην εφαρμογή σας.",
|
||||
"internalErrorTitle": "Εσωτερικό Σφάλμα Διακομιστή",
|
||||
"internalErrorSubtitle": "Παρουσιάστηκε σφάλμα στο διακομιστή και δεν μπορεί να εξυπηρετήσει το αίτημά σας.",
|
||||
"internalErrorButton": "Προσπαθήστε ξανά",
|
||||
"logoutFailTitle": "Αποτυχία αποσύνδεσης",
|
||||
"logoutFailSubtitle": "Παρακαλώ δοκιμάστε ξανά",
|
||||
"logoutSuccessTitle": "Αποσυνδεδεμένος",
|
||||
"logoutSuccessSubtitle": "Έχετε αποσυνδεθεί",
|
||||
"logoutTitle": "Αποσύνδεση",
|
||||
"logoutUsernameSubtitle": "Αυτή τη στιγμή είστε συνδεδεμένοι ως <Code>{{username}}</Code>, κάντε κλικ στο παρακάτω κουμπί για να αποσυνδεθείτε.",
|
||||
"logoutOauthSubtitle": "Αυτή τη στιγμή είστε συνδεδεμένοι ως <Code>{{username}}</Code> χρησιμοποιώντας την υπηρεσία παροχής {{provider}} OAuth, κάντε κλικ στο παρακάτω κουμπί για να αποσυνδεθείτε.",
|
||||
"notFoundTitle": "Η σελίδα δε βρέθηκε",
|
||||
"notFoundSubtitle": "Η σελίδα που ψάχνετε δεν υπάρχει.",
|
||||
"notFoundButton": "Μετάβαση στην αρχική",
|
||||
"totpFailTitle": "Αποτυχία επαλήθευσης κωδικού",
|
||||
"totpFailSubtitle": "Παρακαλώ ελέγξτε τον κώδικά σας και προσπαθήστε ξανά",
|
||||
"totpSuccessTitle": "Επαληθεύθηκε",
|
||||
"totpSuccessSubtitle": "Ανακατεύθυνση στην εφαρμογή σας",
|
||||
"totpTitle": "Εισάγετε τον κωδικό TOTP",
|
||||
"unauthorizedTitle": "Μη εξουσιοδοτημένο",
|
||||
"unauthorizedResourceSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}<Code/> δεν έχει άδεια πρόσβασης στον πόρο <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}<Code/> δεν είναι εξουσιοδοτημένος να συνδεθεί.",
|
||||
"unauthorizedButton": "Προσπαθήστε ξανά"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/en-US.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/en.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/es-ES.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/fi-FI.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/fr-FR.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Bienvenue, connectez-vous avec",
|
||||
"loginDivider": "Ou continuez avec le mot de passe",
|
||||
"loginUsername": "Nom d'utilisateur",
|
||||
"loginPassword": "Mot de passe",
|
||||
"loginSubmit": "Se connecter",
|
||||
"loginFailTitle": "Échec de la connexion",
|
||||
"loginFailSubtitle": "Veuillez vérifier votre nom d'utilisateur et votre mot de passe",
|
||||
"loginFailRateLimit": "Vous n'avez pas pu vous connecter trop de fois, veuillez réessayer plus tard",
|
||||
"loginSuccessTitle": "Connecté",
|
||||
"loginSuccessSubtitle": "Bienvenue!",
|
||||
"loginOauthFailTitle": "Erreur interne",
|
||||
"loginOauthFailSubtitle": "Impossible d'obtenir l'URL OAuth",
|
||||
"loginOauthSuccessTitle": "Redirection",
|
||||
"loginOauthSuccessSubtitle": "Redirection vers votre fournisseur OAuth",
|
||||
"continueRedirectingTitle": "Redirection...",
|
||||
"continueRedirectingSubtitle": "Vous devriez être redirigé vers l'application bientôt",
|
||||
"continueInvalidRedirectTitle": "Redirection invalide",
|
||||
"continueInvalidRedirectSubtitle": "L'URL de redirection est invalide",
|
||||
"continueInsecureRedirectTitle": "Redirection non sécurisée",
|
||||
"continueInsecureRedirectSubtitle": "Vous essayez de rediriger de <Code>https</Code> vers <Code>http</Code>, êtes-vous sûr de vouloir continuer ?",
|
||||
"continueTitle": "Continuer",
|
||||
"continueSubtitle": "Cliquez sur le bouton pour continuer vers votre application.",
|
||||
"internalErrorTitle": "Erreur interne du serveur",
|
||||
"internalErrorSubtitle": "Une erreur s'est produite sur le serveur et il ne peut actuellement pas répondre à votre demande.",
|
||||
"internalErrorButton": "Réessayer",
|
||||
"logoutFailTitle": "Échec de la déconnexion",
|
||||
"logoutFailSubtitle": "Veuillez réessayer",
|
||||
"logoutSuccessTitle": "Déconnecté",
|
||||
"logoutSuccessSubtitle": "Vous avez été déconnecté",
|
||||
"logoutTitle": "Déconnexion",
|
||||
"logoutUsernameSubtitle": "Vous êtes actuellement connecté en tant que <Code>{{username}}</Code>, cliquez sur le bouton ci-dessous pour vous déconnecter.",
|
||||
"logoutOauthSubtitle": "Vous êtes actuellement connecté en tant que <Code>{{username}}</Code> en utilisant le fournisseur OAuth {{provider}} , cliquez sur le bouton ci-dessous pour vous déconnecter.",
|
||||
"notFoundTitle": "Page introuvable",
|
||||
"notFoundSubtitle": "La page recherchée n'existe pas.",
|
||||
"notFoundButton": "Retour à la page d'accueil",
|
||||
"totpFailTitle": "Échec de la vérification du code",
|
||||
"totpFailSubtitle": "Veuillez vérifier votre code et réessayer",
|
||||
"totpSuccessTitle": "Vérifié",
|
||||
"totpSuccessSubtitle": "Redirection vers votre application",
|
||||
"totpTitle": "Saisissez votre code TOTP",
|
||||
"unauthorizedTitle": "Non autorisé",
|
||||
"unauthorizedResourceSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}<Code/> n'est pas autorisé à accéder à la ressource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}<Code/> n'est pas autorisé à se connecter.",
|
||||
"unauthorizedButton": "Réessayer"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/he-IL.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/hu-HU.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/it-IT.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/ja-JP.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/ko-KR.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/nl-NL.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welkom terug, log in met",
|
||||
"loginDivider": "Of ga door met wachtwoord",
|
||||
"loginUsername": "Gebruikersnaam",
|
||||
"loginPassword": "Wachtwoord",
|
||||
"loginSubmit": "Log in",
|
||||
"loginFailTitle": "Mislukt om in te loggen",
|
||||
"loginFailSubtitle": "Gelieve uw gebruikersnaam en wachtwoord te controleren",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/no-NO.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/pl-PL.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Witaj ponownie, zaloguj się przez",
|
||||
"loginDivider": "Lub kontynuuj z hasłem",
|
||||
"loginUsername": "Nazwa użytkownika",
|
||||
"loginPassword": "Hasło",
|
||||
"loginSubmit": "Zaloguj się",
|
||||
"loginFailTitle": "Nie udało się zalogować",
|
||||
"loginFailSubtitle": "Sprawdź swoją nazwę użytkownika i hasło",
|
||||
"loginFailRateLimit": "Nie udało się zalogować zbyt wiele razy, spróbuj ponownie później",
|
||||
"loginSuccessTitle": "Zalogowano",
|
||||
"loginSuccessSubtitle": "Witaj ponownie!",
|
||||
"loginOauthFailTitle": "Wewnętrzny błąd",
|
||||
"loginOauthFailSubtitle": "Nie udało się uzyskać adresu URL OAuth",
|
||||
"loginOauthSuccessTitle": "Przekierowywanie",
|
||||
"loginOauthSuccessSubtitle": "Przekierowywanie do Twojego dostawcy OAuth",
|
||||
"continueRedirectingTitle": "Przekierowywanie...",
|
||||
"continueRedirectingSubtitle": "Wkrótce powinieneś zostać przekierowany do aplikacji",
|
||||
"continueInvalidRedirectTitle": "Nieprawidłowe przekierowanie",
|
||||
"continueInvalidRedirectSubtitle": "Adres przekierowania jest nieprawidłowy",
|
||||
"continueInsecureRedirectTitle": "Niezabezpieczone przekierowanie",
|
||||
"continueInsecureRedirectSubtitle": "Próbujesz przekierować z <Code>https</Code> do <Code>http</Code>, czy na pewno chcesz kontynuować?",
|
||||
"continueTitle": "Kontynuuj",
|
||||
"continueSubtitle": "Kliknij przycisk, aby przejść do aplikacji.",
|
||||
"internalErrorTitle": "Wewnętrzny błąd serwera",
|
||||
"internalErrorSubtitle": "Wystąpił błąd na serwerze i obecnie nie można obsłużyć tego żądania.",
|
||||
"internalErrorButton": "Spróbuj ponownie",
|
||||
"logoutFailTitle": "Nie udało się wylogować",
|
||||
"logoutFailSubtitle": "Spróbuj ponownie",
|
||||
"logoutSuccessTitle": "Wylogowano",
|
||||
"logoutSuccessSubtitle": "Zostałeś wylogowany",
|
||||
"logoutTitle": "Wylogowanie",
|
||||
"logoutUsernameSubtitle": "Jesteś aktualnie zalogowany jako <Code>{{username}}</Code>, kliknij przycisk poniżej, aby się wylogować.",
|
||||
"logoutOauthSubtitle": "Jesteś obecnie zalogowany jako <Code>{{username}}</Code> przy użyciu providera OAuth {{provider}}, kliknij przycisk poniżej, aby się wylogować.",
|
||||
"notFoundTitle": "Strona nie znaleziona",
|
||||
"notFoundSubtitle": "Strona, której szukasz nie istnieje.",
|
||||
"notFoundButton": "Wróć do strony głównej",
|
||||
"totpFailTitle": "Nie udało się zweryfikować kodu",
|
||||
"totpFailSubtitle": "Sprawdź swój kod i spróbuj ponownie",
|
||||
"totpSuccessTitle": "Zweryfikowano",
|
||||
"totpSuccessSubtitle": "Przekierowywanie do aplikacji",
|
||||
"totpTitle": "Wprowadź kod TOTP",
|
||||
"unauthorizedTitle": "Nieautoryzowany",
|
||||
"unauthorizedResourceSubtitle": "Użytkownik o nazwie <Code>{{username}}<Code/> nie jest upoważniony do uzyskania dostępu do zasobu <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "Użytkownik o nazwie <Code>{{username}}<Code/> nie jest upoważniony do logowania.",
|
||||
"unauthorizedButton": "Spróbuj ponownie"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/pt-BR.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/pt-PT.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/ro-RO.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/ru-RU.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/sr-SP.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/sv-SE.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/tr-TR.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/uk-UA.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/vi-VN.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/zh-CN.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
46
frontend/src/lib/i18n/locales/zh-TW.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"loginTitle": "Welcome back, login with",
|
||||
"loginDivider": "Or continue with password",
|
||||
"loginUsername": "Username",
|
||||
"loginPassword": "Password",
|
||||
"loginSubmit": "Login",
|
||||
"loginFailTitle": "Failed to log in",
|
||||
"loginFailSubtitle": "Please check your username and password",
|
||||
"loginFailRateLimit": "You failed to login too many times, please try again later",
|
||||
"loginSuccessTitle": "Logged in",
|
||||
"loginSuccessSubtitle": "Welcome back!",
|
||||
"loginOauthFailTitle": "Internal error",
|
||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||
"loginOauthSuccessTitle": "Redirecting",
|
||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||
"continueRedirectingTitle": "Redirecting...",
|
||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||
"continueInvalidRedirectTitle": "Invalid redirect",
|
||||
"continueInvalidRedirectSubtitle": "The redirect URL is invalid",
|
||||
"continueInsecureRedirectTitle": "Insecure redirect",
|
||||
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
|
||||
"continueTitle": "Continue",
|
||||
"continueSubtitle": "Click the button to continue to your app.",
|
||||
"internalErrorTitle": "Internal Server Error",
|
||||
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
|
||||
"internalErrorButton": "Try again",
|
||||
"logoutFailTitle": "Failed to log out",
|
||||
"logoutFailSubtitle": "Please try again",
|
||||
"logoutSuccessTitle": "Logged out",
|
||||
"logoutSuccessSubtitle": "You have been logged out",
|
||||
"logoutTitle": "Logout",
|
||||
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.",
|
||||
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.",
|
||||
"notFoundTitle": "Page not found",
|
||||
"notFoundSubtitle": "The page you are looking for does not exist.",
|
||||
"notFoundButton": "Go home",
|
||||
"totpFailTitle": "Failed to verify code",
|
||||
"totpFailSubtitle": "Please check your code and try again",
|
||||
"totpSuccessTitle": "Verified",
|
||||
"totpSuccessSubtitle": "Redirecting to your app",
|
||||
"totpTitle": "Enter your TOTP code",
|
||||
"unauthorizedTitle": "Unauthorized",
|
||||
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.",
|
||||
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.",
|
||||
"unauthorizedButton": "Try again"
|
||||
}
|
||||
@@ -16,6 +16,8 @@ import { NotFoundPage } from "./pages/not-found-page.tsx";
|
||||
import { UnauthorizedPage } from "./pages/unauthorized-page.tsx";
|
||||
import { InternalServerError } from "./pages/internal-server-error.tsx";
|
||||
import { TotpPage } from "./pages/totp-page.tsx";
|
||||
import { AppContextProvider } from "./context/app-context.tsx";
|
||||
import "./lib/i18n/i18n.ts";
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
@@ -27,23 +29,25 @@ const queryClient = new QueryClient({
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
<MantineProvider forceColorScheme="dark">
|
||||
<MantineProvider defaultColorScheme="auto">
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Notifications />
|
||||
<UserContextProvider>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/totp" element={<TotpPage />} />
|
||||
<Route path="/logout" element={<LogoutPage />} />
|
||||
<Route path="/continue" element={<ContinuePage />} />
|
||||
<Route path="/unauthorized" element={<UnauthorizedPage />} />
|
||||
<Route path="/error" element={<InternalServerError />} />
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</UserContextProvider>
|
||||
<AppContextProvider>
|
||||
<UserContextProvider>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/totp" element={<TotpPage />} />
|
||||
<Route path="/logout" element={<LogoutPage />} />
|
||||
<Route path="/continue" element={<ContinuePage />} />
|
||||
<Route path="/unauthorized" element={<UnauthorizedPage />} />
|
||||
<Route path="/error" element={<InternalServerError />} />
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</UserContextProvider>
|
||||
</AppContextProvider>
|
||||
</QueryClientProvider>
|
||||
</MantineProvider>
|
||||
</StrictMode>,
|
||||
@@ -5,13 +5,17 @@ import { useUserContext } from "../context/user-context";
|
||||
import { Layout } from "../components/layouts/layout";
|
||||
import { ReactNode } from "react";
|
||||
import { isQueryValid } from "../utils/utils";
|
||||
import { useAppContext } from "../context/app-context";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
export const ContinuePage = () => {
|
||||
const queryString = window.location.search;
|
||||
const params = new URLSearchParams(queryString);
|
||||
const redirectUri = params.get("redirect_uri") ?? "";
|
||||
|
||||
const { isLoggedIn, disableContinue } = useUserContext();
|
||||
const { isLoggedIn } = useUserContext();
|
||||
const { disableContinue } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return <Navigate to={`/login?redirect_uri=${redirectUri}`} />;
|
||||
@@ -23,8 +27,8 @@ export const ContinuePage = () => {
|
||||
|
||||
const redirect = () => {
|
||||
notifications.show({
|
||||
title: "Redirecting",
|
||||
message: "You should be redirected to the app soon",
|
||||
title: t("continueRedirectingTitle"),
|
||||
message: t("continueRedirectingSubtitle"),
|
||||
color: "blue",
|
||||
});
|
||||
setTimeout(() => {
|
||||
@@ -40,12 +44,9 @@ export const ContinuePage = () => {
|
||||
return (
|
||||
<ContinuePageLayout>
|
||||
<Text size="xl" fw={700}>
|
||||
Invalid Redirect
|
||||
</Text>
|
||||
<Text>
|
||||
The redirect URL is invalid, please contact the app owner to fix the
|
||||
issue.
|
||||
{t("Invalid redirect")}
|
||||
</Text>
|
||||
<Text>{t("The redirect URL is invalid")}</Text>
|
||||
</ContinuePageLayout>
|
||||
);
|
||||
}
|
||||
@@ -55,9 +56,9 @@ export const ContinuePage = () => {
|
||||
return (
|
||||
<ContinuePageLayout>
|
||||
<Text size="xl" fw={700}>
|
||||
Redirecting
|
||||
{t("continueRedirectingTitle")}
|
||||
</Text>
|
||||
<Text>You should be redirected to your app soon.</Text>
|
||||
<Text>{t("continueRedirectingSubtitle")}</Text>
|
||||
</ContinuePageLayout>
|
||||
);
|
||||
}
|
||||
@@ -66,14 +67,17 @@ export const ContinuePage = () => {
|
||||
return (
|
||||
<ContinuePageLayout>
|
||||
<Text size="xl" fw={700}>
|
||||
Insecure Redirect
|
||||
{t("continueInsecureRedirectTitle")}
|
||||
</Text>
|
||||
<Text>
|
||||
Your are trying to redirect from <Code>https</Code> to{" "}
|
||||
<Code>http</Code>, are you sure you want to continue?
|
||||
<Trans
|
||||
i18nKey="continueInsecureRedirectSubtitle"
|
||||
t={t}
|
||||
components={{ Code: <Code /> }}
|
||||
/>
|
||||
</Text>
|
||||
<Button fullWidth mt="xl" color="yellow" onClick={redirect}>
|
||||
Continue
|
||||
{t("continueTitle")}
|
||||
</Button>
|
||||
</ContinuePageLayout>
|
||||
);
|
||||
@@ -82,11 +86,11 @@ export const ContinuePage = () => {
|
||||
return (
|
||||
<ContinuePageLayout>
|
||||
<Text size="xl" fw={700}>
|
||||
Continue
|
||||
{t("continueTitle")}
|
||||
</Text>
|
||||
<Text>Click the button to continue to your app.</Text>
|
||||
<Text>{t("continueSubtitle")}</Text>
|
||||
<Button fullWidth mt="xl" onClick={redirect}>
|
||||
Continue
|
||||
{t("continueTitle")}
|
||||
</Button>
|
||||
</ContinuePageLayout>
|
||||
);
|
||||
@@ -1,19 +1,18 @@
|
||||
import { Button, Paper, Text } from "@mantine/core";
|
||||
import { Layout } from "../components/layouts/layout";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const InternalServerError = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Layout>
|
||||
<Paper shadow="md" p={30} mt={30} radius="md" withBorder>
|
||||
<Text size="xl" fw={700}>
|
||||
Internal Server Error
|
||||
</Text>
|
||||
<Text>
|
||||
An error occured on the server and it currently cannot serve your
|
||||
request.
|
||||
{t("internalErrorTitle")}
|
||||
</Text>
|
||||
<Text>{t("internalErrorSubtitle")}</Text>
|
||||
<Button fullWidth mt="xl" onClick={() => window.location.replace("/")}>
|
||||
Try again
|
||||
{t("internalErrorButton")}
|
||||
</Button>
|
||||
</Paper>
|
||||
</Layout>
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Paper, Title, Text, Divider } from "@mantine/core";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import axios, { type AxiosError } from "axios";
|
||||
import { useUserContext } from "../context/user-context";
|
||||
import { Navigate } from "react-router";
|
||||
import { Layout } from "../components/layouts/layout";
|
||||
@@ -9,14 +9,17 @@ import { OAuthButtons } from "../components/auth/oauth-buttons";
|
||||
import { LoginFormValues } from "../schemas/login-schema";
|
||||
import { LoginForm } from "../components/auth/login-forn";
|
||||
import { isQueryValid } from "../utils/utils";
|
||||
import { useAppContext } from "../context/app-context";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const LoginPage = () => {
|
||||
const queryString = window.location.search;
|
||||
const params = new URLSearchParams(queryString);
|
||||
const redirectUri = params.get("redirect_uri") ?? "";
|
||||
|
||||
const { isLoggedIn, configuredProviders, title, genericName } =
|
||||
useUserContext();
|
||||
const { isLoggedIn } = useUserContext();
|
||||
const { configuredProviders, title, genericName } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const oauthProviders = configuredProviders.filter(
|
||||
(value) => value !== "username",
|
||||
@@ -30,10 +33,20 @@ export const LoginPage = () => {
|
||||
mutationFn: (login: LoginFormValues) => {
|
||||
return axios.post("/api/login", login);
|
||||
},
|
||||
onError: () => {
|
||||
onError: (data: AxiosError) => {
|
||||
if (data.response) {
|
||||
if (data.response.status === 429) {
|
||||
notifications.show({
|
||||
title: t("loginFailTitle"),
|
||||
message: t("loginFailRateLimit"),
|
||||
color: "red",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
notifications.show({
|
||||
title: "Failed to login",
|
||||
message: "Check your username and password",
|
||||
title: t("loginFailTitle"),
|
||||
message: t("loginFailSubtitle"),
|
||||
color: "red",
|
||||
});
|
||||
},
|
||||
@@ -44,8 +57,8 @@ export const LoginPage = () => {
|
||||
}
|
||||
|
||||
notifications.show({
|
||||
title: "Logged in",
|
||||
message: "Welcome back!",
|
||||
title: t("loginSuccessTitle"),
|
||||
message: t("loginSuccessSubtitle"),
|
||||
color: "green",
|
||||
});
|
||||
|
||||
@@ -68,15 +81,15 @@ export const LoginPage = () => {
|
||||
},
|
||||
onError: () => {
|
||||
notifications.show({
|
||||
title: "Internal error",
|
||||
message: "Failed to get OAuth URL",
|
||||
title: t("loginOauthFailTitle"),
|
||||
message: t("loginOauthFailSubtitle"),
|
||||
color: "red",
|
||||
});
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
notifications.show({
|
||||
title: "Redirecting",
|
||||
message: "Redirecting to your OAuth provider",
|
||||
title: t("loginOauthSuccessTitle"),
|
||||
message: t("loginOauthSuccessSubtitle"),
|
||||
color: "blue",
|
||||
});
|
||||
setTimeout(() => {
|
||||
@@ -96,7 +109,7 @@ export const LoginPage = () => {
|
||||
{oauthProviders.length > 0 && (
|
||||
<>
|
||||
<Text size="lg" fw={500} ta="center">
|
||||
Welcome back, login with
|
||||
{t("loginTitle")}
|
||||
</Text>
|
||||
<OAuthButtons
|
||||
oauthProviders={oauthProviders}
|
||||
@@ -106,7 +119,7 @@ export const LoginPage = () => {
|
||||
/>
|
||||
{configuredProviders.includes("username") && (
|
||||
<Divider
|
||||
label="Or continue with password"
|
||||
label={t("loginDivider")}
|
||||
labelPosition="center"
|
||||
my="lg"
|
||||
/>
|
||||
@@ -6,9 +6,13 @@ import { useUserContext } from "../context/user-context";
|
||||
import { Navigate } from "react-router";
|
||||
import { Layout } from "../components/layouts/layout";
|
||||
import { capitalize } from "../utils/utils";
|
||||
import { useAppContext } from "../context/app-context";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
export const LogoutPage = () => {
|
||||
const { isLoggedIn, username, oauth, provider, genericName } = useUserContext();
|
||||
const { isLoggedIn, username, oauth, provider } = useUserContext();
|
||||
const { genericName } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return <Navigate to="/login" />;
|
||||
@@ -20,15 +24,15 @@ export const LogoutPage = () => {
|
||||
},
|
||||
onError: () => {
|
||||
notifications.show({
|
||||
title: "Failed to logout",
|
||||
message: "Please try again",
|
||||
title: t("logoutFailTitle"),
|
||||
message: t("logoutFailSubtitle"),
|
||||
color: "red",
|
||||
});
|
||||
},
|
||||
onSuccess: () => {
|
||||
notifications.show({
|
||||
title: "Logged out",
|
||||
message: "Goodbye!",
|
||||
title: t("logoutSuccessTitle"),
|
||||
message: t("logoutSuccessSubtitle"),
|
||||
color: "green",
|
||||
});
|
||||
setTimeout(() => {
|
||||
@@ -41,12 +45,30 @@ export const LogoutPage = () => {
|
||||
<Layout>
|
||||
<Paper shadow="md" p={30} mt={30} radius="md" withBorder>
|
||||
<Text size="xl" fw={700}>
|
||||
Logout
|
||||
{t("logoutTitle")}
|
||||
</Text>
|
||||
<Text>
|
||||
You are currently logged in as <Code>{username}</Code>
|
||||
{oauth && ` using ${capitalize(provider === "generic" ? genericName : provider)} OAuth`}. Click the button
|
||||
below to log out.
|
||||
{oauth ? (
|
||||
<Trans
|
||||
i18nKey="logoutOauthSubtitle"
|
||||
t={t}
|
||||
components={{ Code: <Code /> }}
|
||||
values={{
|
||||
provider:
|
||||
provider === "generic" ? genericName : capitalize(provider),
|
||||
username: username,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Trans
|
||||
i18nKey="logoutUsernameSubtitle"
|
||||
t={t}
|
||||
components={{ Code: <Code /> }}
|
||||
values={{
|
||||
username: username,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Text>
|
||||
<Button
|
||||
fullWidth
|
||||
@@ -54,7 +76,7 @@ export const LogoutPage = () => {
|
||||
onClick={() => logoutMutation.mutate()}
|
||||
loading={logoutMutation.isLoading}
|
||||
>
|
||||
Logout
|
||||
{t("logoutTitle")}
|
||||
</Button>
|
||||
</Paper>
|
||||
</Layout>
|
||||
@@ -1,16 +1,18 @@
|
||||
import { Button, Paper, Text } from "@mantine/core";
|
||||
import { Layout } from "../components/layouts/layout";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const NotFoundPage = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Layout>
|
||||
<Paper shadow="md" p={30} mt={30} radius="md" withBorder>
|
||||
<Text size="xl" fw={700}>
|
||||
Not found
|
||||
{t("notFoundTitle")}
|
||||
</Text>
|
||||
<Text>The page you are looking for does not exist.</Text>
|
||||
<Text>{t("notFoundSubtitle")}</Text>
|
||||
<Button fullWidth mt="xl" onClick={() => window.location.replace("/")}>
|
||||
Go home
|
||||
{t("notFoundButton")}
|
||||
</Button>
|
||||
</Paper>
|
||||
</Layout>
|
||||
@@ -6,13 +6,17 @@ import { TotpForm } from "../components/auth/totp-form";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useAppContext } from "../context/app-context";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const TotpPage = () => {
|
||||
const queryString = window.location.search;
|
||||
const params = new URLSearchParams(queryString);
|
||||
const redirectUri = params.get("redirect_uri") ?? "";
|
||||
|
||||
const { totpPending, isLoggedIn, title } = useUserContext();
|
||||
const { totpPending, isLoggedIn } = useUserContext();
|
||||
const { title } = useAppContext();
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (isLoggedIn) {
|
||||
return <Navigate to={`/logout`} />;
|
||||
@@ -28,15 +32,15 @@ export const TotpPage = () => {
|
||||
},
|
||||
onError: () => {
|
||||
notifications.show({
|
||||
title: "Failed to verify code",
|
||||
message: "Please try again",
|
||||
title: t("totpFailTitle"),
|
||||
message: t("totpFailSubtitle"),
|
||||
color: "red",
|
||||
});
|
||||
},
|
||||
onSuccess: () => {
|
||||
notifications.show({
|
||||
title: "Verified",
|
||||
message: "Redirecting to your app",
|
||||
title: t("totpSuccessTitle"),
|
||||
message: t("totpSuccessSubtitle"),
|
||||
color: "green",
|
||||
});
|
||||
setTimeout(() => {
|
||||
@@ -50,7 +54,7 @@ export const TotpPage = () => {
|
||||
<Title ta="center">{title}</Title>
|
||||
<Paper shadow="md" p="xl" mt={30} radius="md" withBorder>
|
||||
<Text size="lg" fw={500} mb="md" ta="center">
|
||||
Enter your TOTP code
|
||||
{t("totpTitle")}
|
||||
</Text>
|
||||
<TotpForm
|
||||
isLoading={totpMutation.isLoading}
|
||||
@@ -2,6 +2,7 @@ import { Button, Code, Paper, Text } from "@mantine/core";
|
||||
import { Layout } from "../components/layouts/layout";
|
||||
import { Navigate } from "react-router";
|
||||
import { isQueryValid } from "../utils/utils";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
export const UnauthorizedPage = () => {
|
||||
const queryString = window.location.search;
|
||||
@@ -9,6 +10,8 @@ export const UnauthorizedPage = () => {
|
||||
const username = params.get("username") ?? "";
|
||||
const resource = params.get("resource") ?? "";
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!isQueryValid(username)) {
|
||||
return <Navigate to="/" />;
|
||||
}
|
||||
@@ -17,16 +20,27 @@ export const UnauthorizedPage = () => {
|
||||
<Layout>
|
||||
<Paper shadow="md" p={30} mt={30} radius="md" withBorder>
|
||||
<Text size="xl" fw={700}>
|
||||
Unauthorized
|
||||
{t("Unauthorized")}
|
||||
</Text>
|
||||
<Text>
|
||||
The user with username <Code>{username}</Code> is not authorized to{" "}
|
||||
{isQueryValid(resource) ? (
|
||||
<span>
|
||||
access the <Code>{resource}</Code> resource.
|
||||
</span>
|
||||
<Text>
|
||||
<Trans
|
||||
i18nKey="unauthorizedResourceSubtitle"
|
||||
t={t}
|
||||
components={{ Code: <Code /> }}
|
||||
values={{ resource, username }}
|
||||
/>
|
||||
</Text>
|
||||
) : (
|
||||
"login."
|
||||
<Text>
|
||||
<Trans
|
||||
i18nKey="unauthorizedLoginSubtitle"
|
||||
t={t}
|
||||
components={{ Code: <Code /> }}
|
||||
values={{ username }}
|
||||
/>
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
<Button
|
||||
@@ -34,7 +48,7 @@ export const UnauthorizedPage = () => {
|
||||
mt="xl"
|
||||
onClick={() => window.location.replace("/login")}
|
||||
>
|
||||
Try again
|
||||
{t("unauthorizedButton")}
|
||||
</Button>
|
||||
</Paper>
|
||||
</Layout>
|
||||
10
frontend/src/schemas/app-context-schema.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const appContextSchema = z.object({
|
||||
configuredProviders: z.array(z.string()),
|
||||
disableContinue: z.boolean(),
|
||||
title: z.string(),
|
||||
genericName: z.string(),
|
||||
});
|
||||
|
||||
export type AppContextSchemaType = z.infer<typeof appContextSchema>;
|
||||
@@ -5,10 +5,6 @@ export const userContextSchema = z.object({
|
||||
username: z.string(),
|
||||
oauth: z.boolean(),
|
||||
provider: z.string(),
|
||||
configuredProviders: z.array(z.string()),
|
||||
disableContinue: z.boolean(),
|
||||
title: z.string(),
|
||||
genericName: z.string(),
|
||||
totpPending: z.boolean(),
|
||||
});
|
||||
|
||||
17
frontend/vite.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://tinyauth-backend:3000/api",
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ""),
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||