mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26eff02943 | ||
|
|
67246efafc | ||
|
|
260b3ba1f9 | ||
|
|
90f8885e73 | ||
|
|
0bacd6a65c | ||
|
|
2846deb4d0 | ||
|
|
ae6dbcab25 | ||
|
|
d823ec2e82 | ||
|
|
b0ce5273ce | ||
|
|
b576862024 | ||
|
|
dc2496ee18 | ||
|
|
87db207833 | ||
|
|
b1ccf6ec5f | ||
|
|
ca3f63af81 | ||
|
|
29b0ca5bf5 | ||
|
|
da50193e8c | ||
|
|
1829c1fc1c | ||
|
|
79a17407bd | ||
|
|
dd1e3283d5 | ||
|
|
d1baedd092 | ||
|
|
9e504cefc1 | ||
|
|
3cc5ab70fa | ||
|
|
0441af78a1 | ||
|
|
3c615a4fd7 | ||
|
|
8681e16fde | ||
|
|
918c6c448c | ||
|
|
1e2b4f16b3 | ||
|
|
f0d02eb8b3 | ||
|
|
96274206c5 | ||
|
|
7ae7ea6f79 | ||
|
|
487e7ad83d | ||
|
|
c9dabd39fe | ||
|
|
30ab689c6e | ||
|
|
8143af18af | ||
|
|
471c65cc48 | ||
|
|
9162d5afcc | ||
|
|
9236c664c2 | ||
|
|
767ed69020 | ||
|
|
93c706719f | ||
|
|
ed1ac1926b | ||
|
|
3adb6d01c0 | ||
|
|
9c195c1f30 | ||
|
|
e27093404b | ||
|
|
9602427ca0 | ||
|
|
46812cb5ff | ||
|
|
6258d92464 | ||
|
|
c57d913c46 | ||
|
|
5d285b7ac6 | ||
|
|
c96219f851 | ||
|
|
c7d949b1d1 | ||
|
|
6593a8e324 | ||
|
|
0988fa0115 | ||
|
|
9f0b1a172c | ||
|
|
0126a829dd | ||
|
|
b04a2750a3 | ||
|
|
9e22160b16 | ||
|
|
e8e4b093d2 | ||
|
|
0cb27f2637 | ||
|
|
1497c7385b | ||
|
|
327b06d2a1 | ||
|
|
2c2cfc0b73 | ||
|
|
93327b0a6a | ||
|
|
11c74d4117 | ||
|
|
f93bb6e33e | ||
|
|
73699bbda9 | ||
|
|
5f24ec2d86 | ||
|
|
c08bb8f8e0 | ||
|
|
d01e705866 | ||
|
|
ecc80c3c01 | ||
|
|
784a907384 | ||
|
|
b68a5f32ca | ||
|
|
467f9e2ffd | ||
|
|
1d25acde61 | ||
|
|
cb6ac86bb0 | ||
|
|
cd2da4441d | ||
|
|
d7515b172c | ||
|
|
e4de903cc9 | ||
|
|
45166a3b9c | ||
|
|
687b59e397 | ||
|
|
9a3be8d5a5 | ||
|
|
0c23c24651 | ||
|
|
e20e7f016c | ||
|
|
a0c1a24ba9 | ||
|
|
0475b3d01a | ||
|
|
39cb9b99f6 | ||
|
|
efd123ddfe | ||
|
|
413318886c | ||
|
|
187f9d2386 | ||
|
|
5f61c7baa6 | ||
|
|
e3c5ec9a58 | ||
|
|
fde52415cb | ||
|
|
d92225d093 | ||
|
|
e2a0932229 | ||
|
|
6acdf73557 | ||
|
|
4405b61e03 | ||
|
|
fba714d679 | ||
|
|
8e15282405 | ||
|
|
72b5816451 | ||
|
|
4016191497 | ||
|
|
94b36c1622 | ||
|
|
aa736b8b4a | ||
|
|
94cd937c56 | ||
|
|
9ccf3b7242 | ||
|
|
01b7a58c75 | ||
|
|
2b6c661fea | ||
|
|
fbd61cdf2f | ||
|
|
e9534181da | ||
|
|
fdc613fe8e | ||
|
|
fca09a84cb | ||
|
|
6ec6fb1f11 | ||
|
|
3d51d3548f | ||
|
|
8479b6c822 | ||
|
|
21bb165103 | ||
|
|
c7ca01c0fb | ||
|
|
5d2172f2bf | ||
|
|
1decd9d7a5 | ||
|
|
d0091cfed3 | ||
|
|
0ebcf1729c | ||
|
|
51d101e8f0 | ||
|
|
2d9bf225a0 | ||
|
|
f85d12448c | ||
|
|
82c8ad98b4 | ||
|
|
2499c7c426 | ||
|
|
da530217ab | ||
|
|
4c70d8aac7 | ||
|
|
ba1dbcc9f3 | ||
|
|
0ad380cc81 | ||
|
|
b77a2e374e | ||
|
|
647108e8ee | ||
|
|
8e4e41ca07 | ||
|
|
a8eb3f5f1a | ||
|
|
eb5ac5c8ea | ||
|
|
29344e0c48 | ||
|
|
73f7ac057c | ||
|
|
d359d53fb7 | ||
|
|
ff83ee9c62 | ||
|
|
da3a8d7fbe | ||
|
|
ba2fc55188 | ||
|
|
a6896c9b50 | ||
|
|
f2563c5e8d | ||
|
|
9326291780 | ||
|
|
8dd74f310d | ||
|
|
44ed9e9ca6 | ||
|
|
ab22680625 | ||
|
|
a73e29efc9 | ||
|
|
26273b7240 | ||
|
|
c925407f05 | ||
|
|
622fc369d0 | ||
|
|
0c4b7573cc | ||
|
|
401ce5c290 | ||
|
|
9580ad3934 | ||
|
|
b33c0810d5 | ||
|
|
e3e84e0d18 | ||
|
|
55cb4a7144 | ||
|
|
8fbe639aa1 | ||
|
|
9bc042e2cd | ||
|
|
67302c6a17 | ||
|
|
d7e2ec3b08 | ||
|
|
fdaca0e8a7 | ||
|
|
3af5f89606 | ||
|
|
3ccccfc5e5 | ||
|
|
7959f903bb | ||
|
|
59eb91f28f | ||
|
|
9aecfb22a5 | ||
|
|
ba4476a3a7 | ||
|
|
64724d2a92 | ||
|
|
be58ebdb7d | ||
|
|
746a9f5eb6 | ||
|
|
91a6d672fa | ||
|
|
50809476fa | ||
|
|
483f9cf192 | ||
|
|
f1c7dcda45 | ||
|
|
282ffb543c | ||
|
|
c840196a14 | ||
|
|
7d76037a43 | ||
|
|
68fea480ae | ||
|
|
b063e5c276 | ||
|
|
f2d98505f1 | ||
|
|
ec64ea8229 | ||
|
|
59dc181625 | ||
|
|
79387b83f6 | ||
|
|
b13433e554 | ||
|
|
d9fd382cb2 | ||
|
|
baa5e300c8 | ||
|
|
51747773ba | ||
|
|
4d324d885d | ||
|
|
af1a437523 | ||
|
|
dd8663591e | ||
|
|
de4f31f255 | ||
|
|
53c1bc9e59 | ||
|
|
77901ae1bc | ||
|
|
1aeff542e5 | ||
|
|
c05469d704 | ||
|
|
1e6bb542f9 | ||
|
|
b81ad079ee | ||
|
|
044edcda07 | ||
|
|
7eb85d2a99 | ||
|
|
7d4d6cd9df | ||
|
|
3f7eb4528e | ||
|
|
0008f70552 | ||
|
|
ff48517b43 | ||
|
|
2695715180 | ||
|
|
b3be1a561c | ||
|
|
cab7a7f8bb | ||
|
|
f6da5961c3 | ||
|
|
d297f1a27b | ||
|
|
b513509b01 | ||
|
|
bfa3f7e04e | ||
|
|
539b516e45 | ||
|
|
25744efc66 | ||
|
|
21e37366fb | ||
|
|
6002ad44e1 | ||
|
|
a431270718 | ||
|
|
3f6a47c39d | ||
|
|
4dace0a32b | ||
|
|
efb5318fc5 | ||
|
|
a5e83f3133 | ||
|
|
7b6ca12744 | ||
|
|
da1b5a969c | ||
|
|
5d790231ea | ||
|
|
ad363ce38c | ||
|
|
8e5d6eec6c | ||
|
|
92f1400a35 | ||
|
|
47f1938136 | ||
|
|
13d3cfb290 | ||
|
|
c657bdfb63 |
@@ -6,7 +6,6 @@ DATABASE_URL=postgresql://devuser:changeme@localhost:5433/devdb?schema=public
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
PROJECT_NAME="Portabase"
|
PROJECT_NAME="Portabase"
|
||||||
PROJECT_DESCRIPTION="Portabase is a powerful database manager"
|
|
||||||
PROJECT_URL=http://localhost:8887
|
PROJECT_URL=http://localhost:8887
|
||||||
PROJECT_SECRET=
|
PROJECT_SECRET=
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ representative at an online or offline event.
|
|||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community leaders responsible for enforcement at
|
reported to the community leaders responsible for enforcement at
|
||||||
contact@soluce-technologies.com.
|
contact@portabase.io.
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
All community leaders are obligated to respect the privacy and security of the
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ We take security seriously and aim to support the following versions of the proj
|
|||||||
If you discover a security vulnerability in this project, we appreciate your help in disclosing it responsibly.
|
If you discover a security vulnerability in this project, we appreciate your help in disclosing it responsibly.
|
||||||
|
|
||||||
1. **Contact Us**
|
1. **Contact Us**
|
||||||
Please report the vulnerability by emailing **[contact@soluce-technologies.com](mailto:contact@soluce-technologies.com)**. Include the following details:
|
Please report the vulnerability by emailing **[contact@portabase.io](mailto:contact@portabase.io)**. Include the following details:
|
||||||
- A detailed description of the issue.
|
- A detailed description of the issue.
|
||||||
- Steps to reproduce the vulnerability (if applicable).
|
- Steps to reproduce the vulnerability (if applicable).
|
||||||
- Any potential impacts or risks.
|
- Any potential impacts or risks.
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
name: Auto Release bot
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [ closed ]
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
if: github.event.pull_request.merged == true
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/create-github-app-token@v1
|
|
||||||
id: app-token
|
|
||||||
with:
|
|
||||||
app-id: ${{ vars.APP_ID }}
|
|
||||||
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ steps.app-token.outputs.token }}
|
|
||||||
ref: ${{ github.base_ref }}
|
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "lts/*"
|
|
||||||
cache: "pnpm"
|
|
||||||
|
|
||||||
- run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- run: git config --global user.name 'github-actions[bot]'
|
|
||||||
- run: git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
|
||||||
|
|
||||||
- name: Push tags
|
|
||||||
run: git push origin --tags
|
|
||||||
|
|
||||||
- name: Run release-it
|
|
||||||
run: |
|
|
||||||
git pull origin main
|
|
||||||
pnpm exec release-it --ci
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
||||||
@@ -3,6 +3,9 @@ name: Discord Notification
|
|||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
|
release_tag:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
discord_title:
|
discord_title:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
@@ -27,10 +30,10 @@ jobs:
|
|||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
RELEASE_INFO=$(gh release view "${{ github.ref_name }}" -R ${{ github.repository }} --json name,url,body,author)
|
RELEASE_INFO=$(gh release view "${{ inputs.release_tag }}" -R ${{ github.repository }} --json name,url,body,author)
|
||||||
|
|
||||||
RELEASE_TITLE=$(echo "$RELEASE_INFO" | jq -r .name)
|
RELEASE_TITLE=$(echo "$RELEASE_INFO" | jq -r .name)
|
||||||
if [ -z "$RELEASE_TITLE" ] || [ "$RELEASE_TITLE" = "null" ]; then RELEASE_TITLE="${{ github.ref_name }}"; fi
|
if [ -z "$RELEASE_TITLE" ] || [ "$RELEASE_TITLE" = "null" ]; then RELEASE_TITLE="${{ inputs.release_tag }}"; fi
|
||||||
|
|
||||||
RELEASE_URL=$(echo "$RELEASE_INFO" | jq -r .url)
|
RELEASE_URL=$(echo "$RELEASE_INFO" | jq -r .url)
|
||||||
RELEASE_BODY=$(echo "$RELEASE_INFO" | jq -r .body)
|
RELEASE_BODY=$(echo "$RELEASE_INFO" | jq -r .body)
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ name: Docker Publish
|
|||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
|
version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
ref:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
image_name:
|
image_name:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
@@ -32,10 +38,13 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform: [linux/amd64, linux/arm64]
|
platform: [ linux/amd64, linux/arm64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -50,9 +59,10 @@ jobs:
|
|||||||
id: prep
|
id: prep
|
||||||
run: |
|
run: |
|
||||||
ARCH=${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
|
ARCH=${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
|
||||||
echo "image=${{ inputs.image_name }}:${GITHUB_REF#refs/tags/}-$ARCH" >> $GITHUB_OUTPUT
|
echo "image=${{ inputs.image_name }}:${{inputs.version}}-$ARCH" >> $GITHUB_OUTPUT
|
||||||
echo "safe_platform=${{ matrix.platform == 'linux/amd64' && 'linux-amd64' || 'linux-arm64' }}" >> $GITHUB_OUTPUT
|
echo "safe_platform=${{ matrix.platform == 'linux/amd64' && 'linux-amd64' || 'linux-arm64' }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
|
||||||
- name: Build and push image
|
- name: Build and push image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
@@ -95,20 +105,38 @@ jobs:
|
|||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Generate semantic tags
|
||||||
|
id: tags
|
||||||
|
run: |
|
||||||
|
VERSION="${{ inputs.version }}"
|
||||||
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
|
||||||
|
|
||||||
|
echo "VERSION_TAG=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
if [ -n "$MINOR" ]; then
|
||||||
|
echo "MINOR_TAG=$MAJOR.$MINOR" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
if [ -n "$MAJOR" ]; then
|
||||||
|
echo "MAJOR_TAG=$MAJOR" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
if [ "${{ inputs.add_latest }}" = "true" ]; then
|
||||||
|
echo "LATEST_TAG=latest" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Extract Docker metadata
|
- name: Extract Docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ inputs.image_name }}
|
images: ${{ inputs.image_name }}
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}}
|
type=raw,value=${{ steps.tags.outputs.VERSION_TAG }}
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
type=raw,value=${{ steps.tags.outputs.MINOR_TAG }}
|
||||||
type=semver,pattern={{major}}
|
type=raw,value=${{ steps.tags.outputs.MAJOR_TAG }}
|
||||||
type=raw,value=latest,enable=${{ inputs.add_latest }}
|
type=raw,value=${{ steps.tags.outputs.LATEST_TAG }}
|
||||||
|
|
||||||
- name: Create and push manifest list
|
- name: Create and push manifest list
|
||||||
working-directory: /tmp/digests
|
working-directory: /tmp/digests
|
||||||
run: |
|
run: |
|
||||||
DOCKER_IMAGES="$(cat amd64/image.txt) $(cat arm64/image.txt)"
|
DOCKER_IMAGES="$(cat amd64/image.txt) $(cat arm64/image.txt)"
|
||||||
TAG_ARGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
TAG_ARGS=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON")
|
||||||
docker buildx imagetools create $DOCKER_IMAGES $TAG_ARGS
|
echo $TAG_ARGS
|
||||||
|
docker buildx imagetools create $TAG_ARGS $DOCKER_IMAGES
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
name: End-to-end testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: 'pnpm'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/dockerfile/Dockerfile
|
||||||
|
push: false
|
||||||
|
load: true
|
||||||
|
tags: portabase/portabase:test
|
||||||
|
|
||||||
|
- name: Run app container
|
||||||
|
run: |
|
||||||
|
docker run -d --name myapp \
|
||||||
|
-p 8887:80 \
|
||||||
|
-e NODE_ENV=production \
|
||||||
|
-e PROJECT_URL=http://localhost:8887 \
|
||||||
|
-e PROJECT_SECRET=80af5e4c875dfded3a6ba511c6ed8b0160cad723f848ee0851403ed6141f6ba1 \
|
||||||
|
portabase/portabase:test
|
||||||
|
|
||||||
|
- name: Wait for app port to be listening
|
||||||
|
run: |
|
||||||
|
for i in {1..40}; do
|
||||||
|
|
||||||
|
if curl -fsS http://localhost:8887/ >/dev/null; then
|
||||||
|
echo "App is responding on port 8887"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "Waiting for app readiness... ($i/40)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "Timeout: app never became ready"
|
||||||
|
docker logs myapp
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
- name: Install Playwright browsers
|
||||||
|
run: pnpm exec playwright install --with-deps chromium firefox webkit
|
||||||
|
|
||||||
|
- name: Run Playwright tests
|
||||||
|
run: pnpm exec playwright test --project chromium
|
||||||
|
env:
|
||||||
|
PROJECT_URL: http://localhost:8887
|
||||||
|
|
||||||
|
- name: Upload report if failed
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: docker stop myapp && docker rm myapp || true
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
name: Publish Helm Chart
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
GH_TOKEN:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish-helm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Helm
|
||||||
|
uses: azure/setup-helm@v4
|
||||||
|
|
||||||
|
- name: Package Helm chart
|
||||||
|
run: |
|
||||||
|
mkdir -p ./helm-packages
|
||||||
|
helm package helm \
|
||||||
|
--version ${{ inputs.version }} \
|
||||||
|
--app-version ${{ inputs.version }} \
|
||||||
|
--destination ./helm-packages
|
||||||
|
|
||||||
|
- name: Authenticate to GitHub Packages
|
||||||
|
run: |
|
||||||
|
echo "${{ secrets.GH_TOKEN }}" | helm registry login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||||
|
|
||||||
|
- name: Push Helm chart to GitHub Packages (OCI)
|
||||||
|
run: |
|
||||||
|
helm push ./helm-packages/portabase-${{ inputs.version }}.tgz oci://ghcr.io/portabase/charts
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
name: PR Checker
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
validate-code:
|
|
||||||
name: Code Integrity Check
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "lts/*"
|
|
||||||
cache: "pnpm"
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Check code formatting
|
|
||||||
run: pnpm run format:check
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Run Linter
|
|
||||||
run: pnpm run lint
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Type Check
|
|
||||||
run: pnpm run typecheck
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Run Tests
|
|
||||||
run: pnpm run test
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: Test Application Build
|
|
||||||
run: pnpm run build
|
|
||||||
+120
-22
@@ -1,45 +1,143 @@
|
|||||||
name: Publish Docker image for release
|
name: Auto Release & Publish
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
pull_request:
|
||||||
tags:
|
types: [ closed ]
|
||||||
- "*.*.*"
|
branches:
|
||||||
- "!*-*"
|
- main
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker_publish:
|
|
||||||
|
check-skip:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
skip: ${{ steps.set-skip.outputs.skip }}
|
||||||
|
steps:
|
||||||
|
- name: Determine if release should be skipped
|
||||||
|
id: set-skip
|
||||||
|
run: |
|
||||||
|
TITLE="${{ github.event.pull_request.title }}"
|
||||||
|
echo "PR title: $TITLE"
|
||||||
|
|
||||||
|
if [[ "$TITLE" == *"[skip-release]"* ]]; then
|
||||||
|
echo "PR title contains [skip-release], skipping release jobs."
|
||||||
|
echo "skip=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "skip=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
create-release:
|
||||||
|
needs: check-skip
|
||||||
|
if: ${{ needs.check-skip.outputs.skip == 'false' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
draft_tag: ${{ steps.release_step.outputs.draft_tag }}
|
||||||
|
version: ${{ steps.release_step.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/create-github-app-token@v1
|
||||||
|
id: app-token
|
||||||
|
with:
|
||||||
|
app-id: ${{ vars.APP_ID }}
|
||||||
|
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
ref: main
|
||||||
|
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "lts/*"
|
||||||
|
cache: "pnpm"
|
||||||
|
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
git config --global user.name 'github-actions[bot]'
|
||||||
|
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||||
|
|
||||||
|
|
||||||
|
- name: Run release-it
|
||||||
|
id: release_step
|
||||||
|
run: |
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
VERSION=$(pnpm exec release-it --ci --release-version)
|
||||||
|
echo $VERSION
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
OUTPUT=$(pnpm exec release-it --ci)
|
||||||
|
echo "$OUTPUT"
|
||||||
|
|
||||||
|
DRAFT_TAG=$(echo "$OUTPUT" | grep -oE 'untagged-[a-z0-9]+')
|
||||||
|
echo $DRAFT_TAG
|
||||||
|
echo "draft_tag=$DRAFT_TAG" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
|
||||||
|
|
||||||
|
publish-docker:
|
||||||
|
needs: create-release
|
||||||
|
if: ${{ needs.create-release.result == 'success' }}
|
||||||
uses: ./.github/workflows/docker.yml
|
uses: ./.github/workflows/docker.yml
|
||||||
with:
|
with:
|
||||||
|
version: ${{ needs.create-release.outputs.version }}
|
||||||
|
ref: ${{ needs.create-release.outputs.version }}
|
||||||
add_latest: true
|
add_latest: true
|
||||||
secrets:
|
secrets:
|
||||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME_2 }}
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME_2 }}
|
||||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD_2 }}
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD_2 }}
|
||||||
publish_release:
|
|
||||||
needs: docker_publish
|
publish-helm:
|
||||||
|
needs: create-release
|
||||||
|
if: ${{ needs.create-release.result == 'success' }}
|
||||||
|
uses: ./.github/workflows/helm.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.create-release.outputs.version }}
|
||||||
|
secrets:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
finalize-release:
|
||||||
|
needs:
|
||||||
|
- create-release
|
||||||
|
- publish-docker
|
||||||
|
- publish-helm
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
release_tag: ${{ steps.publish_release_step.outputs.release_tag }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v4
|
with:
|
||||||
with:
|
fetch-depth: 0
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Publish GitHub Release
|
- name: Publish GitHub Release
|
||||||
env:
|
id: publish_release_step
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
env:
|
||||||
run: |
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
gh release edit v${{ github.ref_name }} --draft=false
|
run: |
|
||||||
|
OUTPUT=$(gh release edit ${{ needs.create-release.outputs.draft_tag }} --draft=false)
|
||||||
|
echo "$OUTPUT"
|
||||||
|
RELEASE_TAG=$(echo "$OUTPUT" | sed -E 's|.*/releases/tag/||')
|
||||||
|
echo "release_tag=$RELEASE_TAG" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
discord_notification:
|
notify-discord:
|
||||||
needs: docker_publish
|
needs:
|
||||||
|
- publish-docker
|
||||||
|
- publish-helm
|
||||||
|
- create-release
|
||||||
|
- finalize-release
|
||||||
uses: ./.github/workflows/discord.yml
|
uses: ./.github/workflows/discord.yml
|
||||||
with:
|
with:
|
||||||
discord_title: "New Release: v${{ github.ref_name }}"
|
release_tag: ${{ needs.create-release.outputs.version }}
|
||||||
|
discord_title: "New Release"
|
||||||
discord_color: 3066993
|
discord_color: 3066993
|
||||||
discord_footer: "Portabase"
|
discord_footer: "Portabase"
|
||||||
secrets:
|
secrets:
|
||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -47,3 +47,10 @@ private/keys/*
|
|||||||
seeds/pocket-id/*.db
|
seeds/pocket-id/*.db
|
||||||
|
|
||||||
certificates
|
certificates
|
||||||
|
|
||||||
|
# Playwright
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
/playwright/.auth/
|
||||||
|
|||||||
+2
-2
@@ -6,10 +6,10 @@
|
|||||||
},
|
},
|
||||||
"git": {
|
"git": {
|
||||||
"commit": true,
|
"commit": true,
|
||||||
"commitMessage": "chore: release v${version}",
|
"commitMessage": "chore: release ${version}",
|
||||||
"requireCleanWorkingDir": true,
|
"requireCleanWorkingDir": true,
|
||||||
"tag": true,
|
"tag": true,
|
||||||
"tagName": "v${version}",
|
"tagName": "${version}",
|
||||||
"push": true
|
"push": true
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
|
|||||||
+1
-1
@@ -31,5 +31,5 @@ keywords:
|
|||||||
- web-ui
|
- web-ui
|
||||||
- agent
|
- agent
|
||||||
license: Apache-2.0
|
license: Apache-2.0
|
||||||
version: 1.4.25
|
version: 1.8.0
|
||||||
date-released: '2026-03-02'
|
date-released: '2026-03-02'
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://hub.docker.com/r/solucetechnologies/portabase)
|
[](https://hub.docker.com/r/portabase/portabase)
|
||||||
[](https://github.com/Portabase/portabase)
|
[](https://github.com/Portabase/portabase)
|
||||||
[](https://www.buymeacoffee.com/portabase)
|
[](https://www.buymeacoffee.com/portabase)
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@
|
|||||||
[](https://www.mysql.com/)
|
[](https://www.mysql.com/)
|
||||||
[](https://mariadb.org/)
|
[](https://mariadb.org/)
|
||||||
[](https://sqlite.org/)
|
[](https://sqlite.org/)
|
||||||
|
[](https://redis.io/)
|
||||||
|
[](https://valkey.io/)
|
||||||
[](https://www.mongodb.com/)
|
[](https://www.mongodb.com/)
|
||||||
[](https://github.com/Portabase/portabase)
|
[](https://github.com/Portabase/portabase)
|
||||||
[](https://github.com/Portabase/portabase)
|
[](https://github.com/Portabase/portabase)
|
||||||
@@ -53,11 +55,26 @@ You have 4 ways to install Portabase:
|
|||||||
- Automated CLI (recommended) - [details](https://portabase.io/docs/dashboard/setup#cli)
|
- Automated CLI (recommended) - [details](https://portabase.io/docs/dashboard/setup#cli)
|
||||||
- Docker Run - [details](https://portabase.io/docs/dashboard/setup#docker)
|
- Docker Run - [details](https://portabase.io/docs/dashboard/setup#docker)
|
||||||
- Docker Compose setup - [details](https://portabase.io/docs/dashboard/setup#docker-compose)
|
- Docker Compose setup - [details](https://portabase.io/docs/dashboard/setup#docker-compose)
|
||||||
- Kubernetes with Helm (soon)
|
- Kubernetes with Helm [details](https://portabase.io/docs/dashboard/setup#helm)
|
||||||
- Development setup - [details](https://portabase.io/docs/dashboard/setup#development)
|
- Development setup - [details](https://portabase.io/docs/dashboard/setup#development)
|
||||||
|
|
||||||
**Ensure Docker is installed on your machine before getting started.**
|
**Ensure Docker is installed on your machine before getting started.**
|
||||||
|
|
||||||
|
## Supported databases
|
||||||
|
|
||||||
|
| Engine | Support | Supported Versions | Restore |
|
||||||
|
|:-------------------|:----------|:----------------------------------|:--------|
|
||||||
|
| **PostgreSQL** | ✅ Stable | 12, 13, 14, 15, 16, 17 et 18 | Yes |
|
||||||
|
| **MySQL** | ✅ Stable | 5.7, 8 et 9 | Yes |
|
||||||
|
| **MariaDB** | ✅ Stable | 10 et 11 | Yes |
|
||||||
|
| **MongoDB** | ✅ Stable | 4, 5, 6, 7 et 8 | Yes |
|
||||||
|
| **SQLite** | ✅ Stable | 3.x | Yes |
|
||||||
|
| **Redis** | ✅ Stable | 2.8+ | No |
|
||||||
|
| **Valkey** | ✅ Stable | 7.2+ | No |
|
||||||
|
| **MSSQL Server** | ❌ Ongoing | - | Yes |
|
||||||
|
|
||||||
|
See the [Database Servers documentation](https://portabase.io/docs/agent/db) for version-specific backup and restore details.
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
[](https://github.com/Portabase/portabase/graphs/contributors)
|
[](https://github.com/Portabase/portabase/graphs/contributors)
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import React, { Suspense } from "react";
|
import React from "react";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { currentUser } from "@/lib/auth/current-user";
|
import { currentUser } from "@/lib/auth/current-user";
|
||||||
import { AuthLogoSection } from "@/components/wrappers/auth/auth-logo-section";
|
import { AuthLogoSection } from "@/components/wrappers/auth/auth-logo-section";
|
||||||
import { env } from "@/env.mjs";
|
|
||||||
import { Heart } from "lucide-react";
|
import { Heart } from "lucide-react";
|
||||||
import { ErrorLayout } from "@/components/wrappers/common/error-layout";
|
|
||||||
|
|
||||||
export default async function Layout({
|
export default async function Layout({
|
||||||
children,
|
children,
|
||||||
@@ -31,7 +29,6 @@ export default async function Layout({
|
|||||||
<Heart className="size-3 fill-red-500 text-red-500 animate-pulse" />{" "}
|
<Heart className="size-3 fill-red-500 text-red-500 animate-pulse" />{" "}
|
||||||
by <span className="font-medium text-foreground">Portabase</span>
|
by <span className="font-medium text-foreground">Portabase</span>
|
||||||
</p>
|
</p>
|
||||||
<p>v{env.NEXT_PUBLIC_PROJECT_VERSION}</p>
|
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ export async function POST(
|
|||||||
await sendNotificationsBackupRestore(database, body.status == "failed" ? "error_restore" : "success_restore");
|
await sendNotificationsBackupRestore(database, body.status == "failed" ? "error_restore" : "success_restore");
|
||||||
|
|
||||||
const response = {
|
const response = {
|
||||||
message: true,
|
status: true,
|
||||||
details: "Restoration successfully updated"
|
message: "Restoration successfully updated"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ import { author, geistMono, poppins } from "@/fonts/fonts";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Providers } from "./providers";
|
import { Providers } from "./providers";
|
||||||
|
|
||||||
const title = process.env.PROJECT_NAME ?? "App Title";
|
const title = process.env.PROJECT_NAME ?? "Portabase";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: {
|
title: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ services:
|
|||||||
# context: .
|
# context: .
|
||||||
# dockerfile: docker/dockerfile/Dockerfile
|
# dockerfile: docker/dockerfile/Dockerfile
|
||||||
# target: prod
|
# target: prod
|
||||||
image: portabase/portabase:1.4.0
|
image: portabase/portabase:1.7.1
|
||||||
ports:
|
ports:
|
||||||
- '8887:80'
|
- '8887:80'
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import {test, expect, Page} from '@playwright/test';
|
||||||
|
|
||||||
|
|
||||||
|
type UserCredentials = {
|
||||||
|
username: string
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const users: Record<string, UserCredentials> = {
|
||||||
|
admin: {username: 'Admin', email: 'admin@example.com', password: 'testPASS123456!'},
|
||||||
|
normal: {username: 'John Doe', email: 'john.doe@example.com', password: 'testPASS123456!'},
|
||||||
|
}
|
||||||
|
|
||||||
|
async function register(page: Page, name: string, email: string, password: string, confirmPassword: string) {
|
||||||
|
await page.locator('input[name="name"]').fill(name)
|
||||||
|
await page.locator('input[name="email"]').fill(email)
|
||||||
|
await page.locator('input[name="password"]').fill(password)
|
||||||
|
await page.locator('input[name="confirmPassword"]').fill(confirmPassword)
|
||||||
|
|
||||||
|
await page.click('button[type="submit"]')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(page: Page, email: string, password: string) {
|
||||||
|
await page.locator('input[name="email"]').fill(email)
|
||||||
|
await page.locator('input[name="password"]').fill(password)
|
||||||
|
|
||||||
|
await page.locator('button:has-text("Login")').click()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
test.describe.serial('User signup and login flows', () => {
|
||||||
|
|
||||||
|
test('Redirect to login if not connected', async ({page}) => {
|
||||||
|
await page.goto('/dashboard/projects');
|
||||||
|
await expect(page).toHaveURL("login?redirect=%2Fdashboard%2Fprojects");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Password too short', async ({page}) => {
|
||||||
|
await page.goto('')
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
let password = '123456'
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, password, password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=Must have at least 8 character')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Password too simple', async ({page}) => {
|
||||||
|
await page.goto('')
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
let password = '12345678'
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, password, password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=Your password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Password and confirm password mismatch', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
let password = 'testPASS123456!'
|
||||||
|
let confirmPassword = 'testPASS123456!!'
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, password, confirmPassword)
|
||||||
|
|
||||||
|
const toast = page.locator('text=The passwords did not match')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Successful register for admin', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, users["admin"].password, users["admin"].password)
|
||||||
|
|
||||||
|
await expect(page).toHaveURL('/login')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('User already exists.', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
await register(page, users["admin"].username, users["admin"].email, users["admin"].password, users["admin"].password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=User already exists. Use another email.')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Successful register for normal', async ({page}) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
await page.click('text=Sign up')
|
||||||
|
await expect(page).toHaveURL('/register')
|
||||||
|
|
||||||
|
await register(page, users["normal"].username, users["normal"].email, users["normal"].password, users["normal"].password)
|
||||||
|
|
||||||
|
await expect(page).toHaveURL('login')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Failed login because account not active', async ({page}) => {
|
||||||
|
await page.goto('/login')
|
||||||
|
await login(page, users["normal"].email, users["normal"].password)
|
||||||
|
|
||||||
|
const toast = page.locator('text=Your account is not active.')
|
||||||
|
await expect(toast).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Successful login', async ({page}) => {
|
||||||
|
await page.goto('login')
|
||||||
|
await login(page, users["admin"].email, users["admin"].password)
|
||||||
|
|
||||||
|
await expect(page).toHaveURL('/dashboard/home')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// test('Successful logout', async ({page}) => {
|
||||||
|
// await logout()
|
||||||
|
// await expect(page).toHaveURL('/login')
|
||||||
|
// })
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
.DS_Store
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
.bzr/
|
||||||
|
.bzrignore
|
||||||
|
.hg/
|
||||||
|
.hgignore
|
||||||
|
.svn/
|
||||||
|
*.swp
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.orig
|
||||||
|
*~
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.tmproj
|
||||||
|
.vscode/
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: v2
|
||||||
|
name: portabase
|
||||||
|
description: Helm chart for Portabase
|
||||||
|
type: application
|
||||||
|
version: 0.0.0
|
||||||
|
appVersion: "latest"
|
||||||
|
keywords:
|
||||||
|
- postgresql
|
||||||
|
- mariadb
|
||||||
|
- mongodb
|
||||||
|
- mysql
|
||||||
|
- sqlite
|
||||||
|
- backup
|
||||||
|
- database
|
||||||
|
- restore
|
||||||
|
home: https://github.com/Portabase/portabase
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- https://github.com/Portabase/portabase
|
||||||
|
- https://github.com/Portabase/portabase/tree/main/helm
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- name: Charles Gauthereau
|
||||||
|
url: https://github.com/RambokDev
|
||||||
|
- name: Killian Larcher
|
||||||
|
url: https://github.com/KillianLarcher
|
||||||
|
|
||||||
|
icon: https://raw.githubusercontent.com/Portabase/portabase/main/.github/assets/logo.png
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Development Notes
|
||||||
|
|
||||||
|
## Check that Kubernetes is reachable locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get nodes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install the local Portabase Helm chart
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm install portabase . \
|
||||||
|
--set project.secret=$(openssl rand -hex 32)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Check the pods
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods
|
||||||
|
```
|
||||||
|
|
||||||
|
## Check the services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get svc
|
||||||
|
```
|
||||||
|
|
||||||
|
## If the service type is ClusterIP, expose it locally using port forwarding:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl port-forward svc/portabase 8887:80
|
||||||
|
```
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: portabase
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: portabase
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: portabase
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: portabase
|
||||||
|
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
env:
|
||||||
|
- name: TZ
|
||||||
|
value: {{ .Values.timezone }}
|
||||||
|
- name: PROJECT_URL
|
||||||
|
value: {{ .Values.project.url }}
|
||||||
|
- name: PROJECT_SECRET
|
||||||
|
value: {{ .Values.project.secret }}
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: portabase-data
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: portabase-data
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: {{ .Values.persistence.size }}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: portabase
|
||||||
|
spec:
|
||||||
|
type: {{ .Values.service.type }}
|
||||||
|
selector:
|
||||||
|
app: portabase
|
||||||
|
ports:
|
||||||
|
- port: {{ .Values.service.port }}
|
||||||
|
targetPort: 80
|
||||||
|
nodePort: {{ .Values.service.nodePort }}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
image:
|
||||||
|
repository: portabase/portabase
|
||||||
|
tag: latest
|
||||||
|
pullPolicy: Always
|
||||||
|
|
||||||
|
replicaCount: 1
|
||||||
|
|
||||||
|
service:
|
||||||
|
type: ClusterIP
|
||||||
|
port: 80
|
||||||
|
targetPort: 80
|
||||||
|
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "1"
|
||||||
|
limits:
|
||||||
|
memory: "1Gi"
|
||||||
|
cpu: "1"
|
||||||
|
|
||||||
|
timezone: Europe/Paris
|
||||||
|
|
||||||
|
project:
|
||||||
|
url: http://localhost:8887
|
||||||
|
secret: "change_me_generate_secure_secret"
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
storageClassName: ""
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
size: 10Gi
|
||||||
|
mountPath: /data
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
enabled: false
|
||||||
|
className: nginx
|
||||||
|
hosts:
|
||||||
|
- host: portabase.example.com
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls: []
|
||||||
|
|
||||||
|
updateStrategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
partition: 0
|
||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "portabase",
|
"name": "portabase",
|
||||||
"version": "1.4.25",
|
"version": "1.8.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack -p 8887",
|
"dev": "next dev --turbopack -p 8887",
|
||||||
@@ -103,6 +103,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify/react": "^6.0.2",
|
"@iconify/react": "^6.0.2",
|
||||||
|
"@playwright/test": "1.58.2",
|
||||||
"@react-email/preview-server": "4.3.2",
|
"@react-email/preview-server": "4.3.2",
|
||||||
"@react-email/render": "^2.0.4",
|
"@react-email/render": "^2.0.4",
|
||||||
"@release-it/bumper": "^7.0.5",
|
"@release-it/bumper": "^7.0.5",
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
// import dotenv from 'dotenv';
|
||||||
|
// import path from 'path';
|
||||||
|
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './e2e',
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: 'html',
|
||||||
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
use: {
|
||||||
|
/* Base URL to use in actions like `await page.goto('')`. */
|
||||||
|
baseURL: process.env.PROJECT_URL,
|
||||||
|
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'firefox',
|
||||||
|
use: { ...devices['Desktop Firefox'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'webkit',
|
||||||
|
use: { ...devices['Desktop Safari'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Test against mobile viewports. */
|
||||||
|
{
|
||||||
|
name: 'Mobile Chrome',
|
||||||
|
use: { ...devices['Pixel 5'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Mobile Safari',
|
||||||
|
use: { ...devices['iPhone 12'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
// {
|
||||||
|
// name: 'Microsoft Edge',
|
||||||
|
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Google Chrome',
|
||||||
|
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
// webServer: {
|
||||||
|
// command: 'docker compose up -d && make up',
|
||||||
|
// url: 'http://localhost:8887',
|
||||||
|
// reuseExistingServer: !process.env.CI,
|
||||||
|
// },
|
||||||
|
});
|
||||||
Generated
+58
-18
@@ -10,10 +10,10 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/passkey':
|
'@better-auth/passkey':
|
||||||
specifier: ^1.4.19
|
specifier: ^1.4.19
|
||||||
version: 1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)
|
version: 1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)
|
||||||
'@better-auth/sso':
|
'@better-auth/sso':
|
||||||
specifier: ^1.4.19
|
specifier: ^1.4.19
|
||||||
version: 1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))
|
version: 1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))
|
||||||
'@hookform/resolvers':
|
'@hookform/resolvers':
|
||||||
specifier: ^5.2.2
|
specifier: ^5.2.2
|
||||||
version: 5.2.2(react-hook-form@7.71.2(react@19.2.4))
|
version: 5.2.2(react-hook-form@7.71.2(react@19.2.4))
|
||||||
@@ -130,7 +130,7 @@ importers:
|
|||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
better-auth:
|
better-auth:
|
||||||
specifier: 1.4.18
|
specifier: 1.4.18
|
||||||
version: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -175,10 +175,10 @@ importers:
|
|||||||
version: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 12.34.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next:
|
next:
|
||||||
specifier: 16.1.5
|
specifier: 16.1.5
|
||||||
version: 16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
next-safe-action:
|
next-safe-action:
|
||||||
specifier: ^7.10.8
|
specifier: ^7.10.8
|
||||||
version: 7.10.8(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76)
|
version: 7.10.8(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76)
|
||||||
next-themes:
|
next-themes:
|
||||||
specifier: ^0.4.6
|
specifier: ^0.4.6
|
||||||
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -267,9 +267,12 @@ importers:
|
|||||||
'@iconify/react':
|
'@iconify/react':
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2(react@19.2.4)
|
version: 6.0.2(react@19.2.4)
|
||||||
|
'@playwright/test':
|
||||||
|
specifier: 1.58.2
|
||||||
|
version: 1.58.2
|
||||||
'@react-email/preview-server':
|
'@react-email/preview-server':
|
||||||
specifier: 4.3.2
|
specifier: 4.3.2
|
||||||
version: 4.3.2(postcss@8.5.6)
|
version: 4.3.2(@playwright/test@1.58.2)(postcss@8.5.6)
|
||||||
'@react-email/render':
|
'@react-email/render':
|
||||||
specifier: ^2.0.4
|
specifier: ^2.0.4
|
||||||
version: 2.0.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 2.0.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
@@ -1952,6 +1955,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
'@playwright/test@1.58.2':
|
||||||
|
resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
'@prisma/client@6.7.0':
|
'@prisma/client@6.7.0':
|
||||||
resolution: {integrity: sha512-+k61zZn1XHjbZul8q6TdQLpuI/cvyfil87zqK2zpreNIXyXtpUv3+H/oM69hcsFcZXaokHJIzPAt5Z8C8eK2QA==}
|
resolution: {integrity: sha512-+k61zZn1XHjbZul8q6TdQLpuI/cvyfil87zqK2zpreNIXyXtpUv3+H/oM69hcsFcZXaokHJIzPAt5Z8C8eK2QA==}
|
||||||
engines: {node: '>=18.18'}
|
engines: {node: '>=18.18'}
|
||||||
@@ -4909,6 +4917,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
|
resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
|
||||||
engines: {node: '>=14.14'}
|
engines: {node: '>=14.14'}
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
@@ -6215,6 +6228,16 @@ packages:
|
|||||||
pkg-types@2.3.0:
|
pkg-types@2.3.0:
|
||||||
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
|
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
|
||||||
|
|
||||||
|
playwright-core@1.58.2:
|
||||||
|
resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
playwright@1.58.2:
|
||||||
|
resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
pluralize@8.0.0:
|
pluralize@8.0.0:
|
||||||
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -7695,23 +7718,23 @@ snapshots:
|
|||||||
nanostores: 1.1.0
|
nanostores: 1.1.0
|
||||||
zod: 4.3.6
|
zod: 4.3.6
|
||||||
|
|
||||||
'@better-auth/passkey@1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)':
|
'@better-auth/passkey@1.4.19(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))(nanostores@1.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
||||||
'@better-auth/utils': 0.3.0
|
'@better-auth/utils': 0.3.0
|
||||||
'@better-fetch/fetch': 1.1.21
|
'@better-fetch/fetch': 1.1.21
|
||||||
'@simplewebauthn/browser': 13.2.2
|
'@simplewebauthn/browser': 13.2.2
|
||||||
'@simplewebauthn/server': 13.2.3
|
'@simplewebauthn/server': 13.2.3
|
||||||
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
better-call: 1.1.8(zod@3.25.76)
|
better-call: 1.1.8(zod@3.25.76)
|
||||||
nanostores: 1.1.0
|
nanostores: 1.1.0
|
||||||
zod: 4.3.6
|
zod: 4.3.6
|
||||||
|
|
||||||
'@better-auth/sso@1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))':
|
'@better-auth/sso@1.4.19(@better-auth/utils@0.3.0)(better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(better-call@1.1.8(zod@3.25.76))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/utils': 0.3.0
|
'@better-auth/utils': 0.3.0
|
||||||
'@better-fetch/fetch': 1.1.21
|
'@better-fetch/fetch': 1.1.21
|
||||||
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
better-auth: 1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
better-call: 1.1.8(zod@3.25.76)
|
better-call: 1.1.8(zod@3.25.76)
|
||||||
fast-xml-parser: 5.3.9
|
fast-xml-parser: 5.3.9
|
||||||
jose: 6.1.3
|
jose: 6.1.3
|
||||||
@@ -8807,6 +8830,10 @@ snapshots:
|
|||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@playwright/test@1.58.2':
|
||||||
|
dependencies:
|
||||||
|
playwright: 1.58.2
|
||||||
|
|
||||||
'@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)':
|
'@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)':
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
prisma: 6.7.0(typescript@5.9.3)
|
prisma: 6.7.0(typescript@5.9.3)
|
||||||
@@ -10186,7 +10213,7 @@ snapshots:
|
|||||||
md-to-react-email: 5.0.5(react@19.2.4)
|
md-to-react-email: 5.0.5(react@19.2.4)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
|
|
||||||
'@react-email/preview-server@4.3.2(postcss@8.5.6)':
|
'@react-email/preview-server@4.3.2(@playwright/test@1.58.2)(postcss@8.5.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.26.10
|
'@babel/core': 7.26.10
|
||||||
'@babel/parser': 7.27.0
|
'@babel/parser': 7.27.0
|
||||||
@@ -10212,7 +10239,7 @@ snapshots:
|
|||||||
json5: 2.2.3
|
json5: 2.2.3
|
||||||
log-symbols: 4.1.0
|
log-symbols: 4.1.0
|
||||||
module-punycode: punycode@2.3.1
|
module-punycode: punycode@2.3.1
|
||||||
next: 15.5.2(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
next: 15.5.2(@babel/core@7.26.10)(@playwright/test@1.58.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
node-html-parser: 7.0.1
|
node-html-parser: 7.0.1
|
||||||
ora: 5.4.1
|
ora: 5.4.1
|
||||||
pretty-bytes: 6.1.1
|
pretty-bytes: 6.1.1
|
||||||
@@ -11187,7 +11214,7 @@ snapshots:
|
|||||||
|
|
||||||
before-after-hook@4.0.0: {}
|
before-after-hook@4.0.0: {}
|
||||||
|
|
||||||
better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
better-auth@1.4.18(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(drizzle-kit@0.31.9)(drizzle-orm@0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3)))(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
'@better-auth/core': 1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0)
|
||||||
'@better-auth/telemetry': 1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))
|
'@better-auth/telemetry': 1.4.18(@better-auth/core@1.4.18(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.21)(better-call@1.1.8(zod@3.25.76))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.0))
|
||||||
@@ -11205,7 +11232,7 @@ snapshots:
|
|||||||
'@prisma/client': 6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)
|
'@prisma/client': 6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3)
|
||||||
drizzle-kit: 0.31.9
|
drizzle-kit: 0.31.9
|
||||||
drizzle-orm: 0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))
|
drizzle-orm: 0.43.1(@prisma/client@6.7.0(prisma@6.7.0(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(kysely@0.28.11)(pg@8.18.0)(prisma@6.7.0(typescript@5.9.3))
|
||||||
next: 16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
pg: 8.18.0
|
pg: 8.18.0
|
||||||
prisma: 6.7.0(typescript@5.9.3)
|
prisma: 6.7.0(typescript@5.9.3)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
@@ -12564,6 +12591,9 @@ snapshots:
|
|||||||
jsonfile: 6.2.0
|
jsonfile: 6.2.0
|
||||||
universalify: 2.0.1
|
universalify: 2.0.1
|
||||||
|
|
||||||
|
fsevents@2.3.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -13437,9 +13467,9 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 2.19.0
|
type-fest: 2.19.0
|
||||||
|
|
||||||
next-safe-action@7.10.8(next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76):
|
next-safe-action@7.10.8(next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@3.25.76):
|
||||||
dependencies:
|
dependencies:
|
||||||
next: 16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
next: 16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -13450,7 +13480,7 @@ snapshots:
|
|||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
|
|
||||||
next@15.5.2(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
next@15.5.2(@babel/core@7.26.10)(@playwright/test@1.58.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.5.2
|
'@next/env': 15.5.2
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@@ -13468,12 +13498,13 @@ snapshots:
|
|||||||
'@next/swc-linux-x64-musl': 15.5.2
|
'@next/swc-linux-x64-musl': 15.5.2
|
||||||
'@next/swc-win32-arm64-msvc': 15.5.2
|
'@next/swc-win32-arm64-msvc': 15.5.2
|
||||||
'@next/swc-win32-x64-msvc': 15.5.2
|
'@next/swc-win32-x64-msvc': 15.5.2
|
||||||
|
'@playwright/test': 1.58.2
|
||||||
sharp: 0.34.5
|
sharp: 0.34.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
|
|
||||||
next@16.1.5(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
next@16.1.5(@babel/core@7.29.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 16.1.5
|
'@next/env': 16.1.5
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
@@ -13492,6 +13523,7 @@ snapshots:
|
|||||||
'@next/swc-linux-x64-musl': 16.1.5
|
'@next/swc-linux-x64-musl': 16.1.5
|
||||||
'@next/swc-win32-arm64-msvc': 16.1.5
|
'@next/swc-win32-arm64-msvc': 16.1.5
|
||||||
'@next/swc-win32-x64-msvc': 16.1.5
|
'@next/swc-win32-x64-msvc': 16.1.5
|
||||||
|
'@playwright/test': 1.58.2
|
||||||
sharp: 0.34.5
|
sharp: 0.34.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@babel/core'
|
- '@babel/core'
|
||||||
@@ -13860,6 +13892,14 @@ snapshots:
|
|||||||
exsolve: 1.0.8
|
exsolve: 1.0.8
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
playwright-core@1.58.2: {}
|
||||||
|
|
||||||
|
playwright@1.58.2:
|
||||||
|
dependencies:
|
||||||
|
playwright-core: 1.58.2
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.2
|
||||||
|
|
||||||
pluralize@8.0.0: {}
|
pluralize@8.0.0: {}
|
||||||
|
|
||||||
possible-typed-array-names@1.1.0: {}
|
possible-typed-array-names@1.1.0: {}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
+5
-1
@@ -6,6 +6,10 @@ export const S3ChannelConfigSchema = z.object({
|
|||||||
accessKey: z.string().min(1, "Access Key is required"),
|
accessKey: z.string().min(1, "Access Key is required"),
|
||||||
secretKey: z.string().min(1, "Secret Key is required"),
|
secretKey: z.string().min(1, "Secret Key is required"),
|
||||||
bucketName: z.string().min(1, "Bucket name is required"),
|
bucketName: z.string().min(1, "Bucket name is required"),
|
||||||
port: z.coerce.number().optional(),
|
port: z.union([
|
||||||
|
z.literal("").transform(() => ""),
|
||||||
|
z.literal("").transform(() => ""),
|
||||||
|
z.coerce.number()
|
||||||
|
]).optional(),
|
||||||
ssl: z.boolean().optional().default(true),
|
ssl: z.boolean().optional().default(true),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ interface DatabaseActionsCellProps {
|
|||||||
backup: BackupWith;
|
backup: BackupWith;
|
||||||
activeMember: MemberWithUser;
|
activeMember: MemberWithUser;
|
||||||
isAlreadyRestore: boolean;
|
isAlreadyRestore: boolean;
|
||||||
|
isBackupOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DatabaseActionsCell({backup, activeMember, isAlreadyRestore}: DatabaseActionsCellProps) {
|
export function DatabaseActionsCell({backup, activeMember, isAlreadyRestore, isBackupOnly}: DatabaseActionsCellProps) {
|
||||||
const {openModal} = useBackupModal();
|
const {openModal} = useBackupModal();
|
||||||
|
|
||||||
if (backup.deletedAt || activeMember.role === "member") return null;
|
if (backup.deletedAt || activeMember.role === "member") return null;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex items-center space-x-2")}>
|
<div className={cn("flex items-center space-x-2")}>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -40,18 +40,19 @@ export function DatabaseActionsCell({backup, activeMember, isAlreadyRestore}: Da
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||||
|
|
||||||
{backup.status == "success" ? (
|
{backup.status == "success" ? (
|
||||||
<>
|
<>
|
||||||
<TooltipCustom disabled={isAlreadyRestore}
|
{!isBackupOnly && (
|
||||||
text="Already a restoration waiting">
|
<TooltipCustom disabled={isAlreadyRestore}
|
||||||
<DropdownMenuItem
|
text="Already a restoration waiting">
|
||||||
disabled={isAlreadyRestore}
|
<DropdownMenuItem
|
||||||
onSelect={() => openModal("restore", backup)}
|
disabled={isAlreadyRestore}
|
||||||
>
|
onSelect={() => openModal("restore", backup)}
|
||||||
<ReloadIcon/> Restore
|
>
|
||||||
</DropdownMenuItem>
|
<ReloadIcon/> Restore
|
||||||
</TooltipCustom>
|
</DropdownMenuItem>
|
||||||
|
</TooltipCustom>
|
||||||
|
)}
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onSelect={() => openModal("download", backup)}
|
onSelect={() => openModal("download", backup)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
} from "@/components/wrappers/dashboard/admin/channels/helpers/common";
|
} from "@/components/wrappers/dashboard/admin/channels/helpers/common";
|
||||||
import {StorageChannel} from "@/db/schema/12_storage-channel";
|
import {StorageChannel} from "@/db/schema/12_storage-channel";
|
||||||
import {
|
import {
|
||||||
|
EVENT_KIND_BACKUP_ONLY_OPTIONS,
|
||||||
EVENT_KIND_OPTIONS,
|
EVENT_KIND_OPTIONS,
|
||||||
PoliciesSchema,
|
PoliciesSchema,
|
||||||
PoliciesType,
|
PoliciesType,
|
||||||
@@ -31,6 +32,7 @@ import {
|
|||||||
createAlertPoliciesAction, createStoragePoliciesAction, deleteAlertPoliciesAction, deleteStoragePoliciesAction,
|
createAlertPoliciesAction, createStoragePoliciesAction, deleteAlertPoliciesAction, deleteStoragePoliciesAction,
|
||||||
updateAlertPoliciesAction, updateStoragePoliciesAction
|
updateAlertPoliciesAction, updateStoragePoliciesAction
|
||||||
} from "@/components/wrappers/dashboard/database/channels-policy/policy.action";
|
} from "@/components/wrappers/dashboard/database/channels-policy/policy.action";
|
||||||
|
import {backupOnly} from "@/components/wrappers/dashboard/projects/database/database-tabs";
|
||||||
|
|
||||||
type ChannelPoliciesFormProps = {
|
type ChannelPoliciesFormProps = {
|
||||||
onSuccess?: () => void;
|
onSuccess?: () => void;
|
||||||
@@ -53,6 +55,9 @@ export const ChannelPoliciesForm = ({
|
|||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const channelText = getChannelTextBasedOnKind(kind);
|
const channelText = getChannelTextBasedOnKind(kind);
|
||||||
|
|
||||||
|
const isBackupOnly = backupOnly.some((type) => database.dbms === type);
|
||||||
|
|
||||||
|
|
||||||
const organizationChannels = channels.map(c => c.id);
|
const organizationChannels = channels.map(c => c.id);
|
||||||
|
|
||||||
const filterByChannel = <T, K extends keyof T>(
|
const filterByChannel = <T, K extends keyof T>(
|
||||||
@@ -300,7 +305,7 @@ export const ChannelPoliciesForm = ({
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="max-w-full overflow-hidden">
|
<div className="max-w-full overflow-hidden">
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
options={EVENT_KIND_OPTIONS}
|
options={isBackupOnly ? EVENT_KIND_BACKUP_ONLY_OPTIONS : EVENT_KIND_OPTIONS}
|
||||||
onValueChange={field.onChange}
|
onValueChange={field.onChange}
|
||||||
defaultValue={field.value ?? []}
|
defaultValue={field.value ?? []}
|
||||||
placeholder={isMobile ? "Select events..." : "Select events to trigger notifications..."}
|
placeholder={isMobile ? "Select events..." : "Select events to trigger notifications..."}
|
||||||
|
|||||||
@@ -23,5 +23,10 @@ export const EVENT_KIND_OPTIONS = [
|
|||||||
{label: "Error Restore", value: "error_restore"},
|
{label: "Error Restore", value: "error_restore"},
|
||||||
{label: "Success Restore", value: "success_restore"},
|
{label: "Success Restore", value: "success_restore"},
|
||||||
{label: "Success Backup", value: "success_backup"},
|
{label: "Success Backup", value: "success_backup"},
|
||||||
{label: "Weekly Report", value: "weekly_report"},
|
// {label: "Weekly Report", value: "weekly_report"},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const EVENT_KIND_BACKUP_ONLY_OPTIONS = [
|
||||||
|
{label: "Error Backup", value: "error_backup"},
|
||||||
|
{label: "Success Backup", value: "success_backup"},
|
||||||
];
|
];
|
||||||
@@ -18,6 +18,8 @@ export type DatabaseTabsProps = {
|
|||||||
activeMember: MemberWithUser
|
activeMember: MemberWithUser
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const backupOnly = ["redis", "valkey"];
|
||||||
|
|
||||||
export const DatabaseTabs = (props: DatabaseTabsProps) => {
|
export const DatabaseTabs = (props: DatabaseTabsProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
@@ -26,6 +28,7 @@ export const DatabaseTabs = (props: DatabaseTabsProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newTab = searchParams.get("tab") ?? "backup";
|
const newTab = searchParams.get("tab") ?? "backup";
|
||||||
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
setTab(newTab);
|
setTab(newTab);
|
||||||
}, [searchParams]);
|
}, [searchParams]);
|
||||||
|
|
||||||
@@ -33,13 +36,13 @@ export const DatabaseTabs = (props: DatabaseTabsProps) => {
|
|||||||
router.push(`?tab=${value}`);
|
router.push(`?tab=${value}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const isBackupOnly = backupOnly.some((type) => props.database.dbms === type)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs className="flex flex-col flex-1" value={tab} onValueChange={handleChangeTab}>
|
<>
|
||||||
<TabsList className="grid w-full grid-cols-2">
|
{isBackupOnly ?
|
||||||
<TabsTrigger value="backup">Backup</TabsTrigger>
|
|
||||||
<TabsTrigger value="restore">Restoration</TabsTrigger>
|
|
||||||
</TabsList>
|
|
||||||
<TabsContent className="h-full justify-between" value="backup">
|
|
||||||
<DatabaseBackupList
|
<DatabaseBackupList
|
||||||
isAlreadyRestore={props.isAlreadyRestore}
|
isAlreadyRestore={props.isAlreadyRestore}
|
||||||
settings={props.settings}
|
settings={props.settings}
|
||||||
@@ -47,15 +50,33 @@ export const DatabaseTabs = (props: DatabaseTabsProps) => {
|
|||||||
backups={props.backups}
|
backups={props.backups}
|
||||||
activeMember={props.activeMember}
|
activeMember={props.activeMember}
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
:
|
||||||
<TabsContent className="h-full justify-between" value="restore">
|
<Tabs className="flex flex-col flex-1" value={tab} onValueChange={handleChangeTab}>
|
||||||
<DatabaseRestoreList
|
<TabsList className="grid w-full grid-cols-2">
|
||||||
isAlreadyRestore={props.isAlreadyRestore}
|
<TabsTrigger value="backup">Backup</TabsTrigger>
|
||||||
restorations={props.restorations}
|
<TabsTrigger value="restore">Restoration</TabsTrigger>
|
||||||
activeMember={props.activeMember}
|
</TabsList>
|
||||||
databaseId={props.database.id}
|
<TabsContent className="h-full justify-between" value="backup">
|
||||||
/>
|
<DatabaseBackupList
|
||||||
</TabsContent>
|
isAlreadyRestore={props.isAlreadyRestore}
|
||||||
</Tabs>
|
settings={props.settings}
|
||||||
|
database={props.database}
|
||||||
|
backups={props.backups}
|
||||||
|
activeMember={props.activeMember}
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent className="h-full justify-between" value="restore">
|
||||||
|
<DatabaseRestoreList
|
||||||
|
isAlreadyRestore={props.isAlreadyRestore}
|
||||||
|
restorations={props.restorations}
|
||||||
|
activeMember={props.activeMember}
|
||||||
|
databaseId={props.database.id}
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
}
|
||||||
|
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TYPE "public"."dbms_status" ADD VALUE 'redis';
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TYPE "public"."dbms_status" ADD VALUE 'valkey';
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -267,6 +267,20 @@
|
|||||||
"when": 1772200831628,
|
"when": 1772200831628,
|
||||||
"tag": "0037_neat_talon",
|
"tag": "0037_neat_talon",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 38,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1773433992397,
|
||||||
|
"tag": "0038_misty_red_hulk",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 39,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1773659096498,
|
||||||
|
"tag": "0039_conscious_solo",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import {pgEnum} from "drizzle-orm/pg-core";
|
|||||||
import {createSelectSchema} from "drizzle-zod";
|
import {createSelectSchema} from "drizzle-zod";
|
||||||
import {z} from "zod";
|
import {z} from "zod";
|
||||||
|
|
||||||
export const dbmsEnum = pgEnum("dbms_status", ["postgresql", "mysql", "mongodb", "sqlite"]);
|
export const dbmsEnum = pgEnum("dbms_status", ["postgresql", "mysql", "mongodb", "sqlite", "redis", "valkey"]);
|
||||||
export const statusEnum = pgEnum("status", ["waiting", "ongoing", "failed", "success"]);
|
export const statusEnum = pgEnum("status", ["waiting", "ongoing", "failed", "success"]);
|
||||||
export const typeStorageEnum = pgEnum("type_storage", ["local", "s3"]);
|
export const typeStorageEnum = pgEnum("type_storage", ["local", "s3"]);
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {formatLocalizedDate} from "@/utils/date-formatting";
|
|||||||
import {formatBytes} from "@/utils/text";
|
import {formatBytes} from "@/utils/text";
|
||||||
import {DatabaseActionsCell} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions-cell";
|
import {DatabaseActionsCell} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions-cell";
|
||||||
import { Badge as BadgeC } from "@/components/ui/badge";
|
import { Badge as BadgeC } from "@/components/ui/badge";
|
||||||
|
import {backupOnly} from "@/components/wrappers/dashboard/projects/database/database-tabs";
|
||||||
|
|
||||||
export function backupColumns(
|
export function backupColumns(
|
||||||
isAlreadyRestore: boolean,
|
isAlreadyRestore: boolean,
|
||||||
@@ -18,6 +19,10 @@ export function backupColumns(
|
|||||||
database: DatabaseWith,
|
database: DatabaseWith,
|
||||||
activeMember: MemberWithUser
|
activeMember: MemberWithUser
|
||||||
): ColumnDef<Backup>[] {
|
): ColumnDef<Backup>[] {
|
||||||
|
|
||||||
|
const isBackupOnly = backupOnly.some((type) => database.dbms === type)
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: "availability",
|
id: "availability",
|
||||||
@@ -90,137 +95,7 @@ export function backupColumns(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: "actions",
|
||||||
cell: ({row}) => <DatabaseActionsCell isAlreadyRestore={isAlreadyRestore} activeMember={activeMember} backup={row.original}/>,
|
cell: ({row}) => <DatabaseActionsCell isAlreadyRestore={isAlreadyRestore} activeMember={activeMember} backup={row.original} isBackupOnly={isBackupOnly}/>,
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// id: "actions",
|
|
||||||
// cell: ({row, table}) => {
|
|
||||||
// const status = row.getValue("status");
|
|
||||||
// const rowData: Backup = row.original;
|
|
||||||
// const fileName = rowData.file;
|
|
||||||
//
|
|
||||||
// const router = useRouter();
|
|
||||||
//
|
|
||||||
// const mutationRestore = useMutation({
|
|
||||||
// mutationFn: async () => {
|
|
||||||
// const restoration = await createRestorationAction({
|
|
||||||
// backupId: rowData.id,
|
|
||||||
// databaseId: rowData.databaseId,
|
|
||||||
// });
|
|
||||||
// // @ts-ignore
|
|
||||||
// if (restoration.data.success) {
|
|
||||||
// // @ts-ignore
|
|
||||||
// toast.success(restoration.data.actionSuccess?.message || "Restoration created successfully!");
|
|
||||||
// router.refresh();
|
|
||||||
// } else {
|
|
||||||
// // @ts-ignore
|
|
||||||
// toast.error(restoration.serverError || "Failed to create restoration.");
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// const mutationDeleteBackup = useMutation({
|
|
||||||
// mutationFn: async () => {
|
|
||||||
// const deletion = await deleteBackupAction({
|
|
||||||
// backupId: rowData.id,
|
|
||||||
// databaseId: rowData.databaseId,
|
|
||||||
// status: rowData.status,
|
|
||||||
// file: rowData.file ?? "",
|
|
||||||
// projectSlug: database.project?.slug!
|
|
||||||
// });
|
|
||||||
// // @ts-ignore
|
|
||||||
// if (deletion.data.success) {
|
|
||||||
// // @ts-ignore
|
|
||||||
// toast.success(deletion.data.actionSuccess.message);
|
|
||||||
// router.refresh();
|
|
||||||
// } else {
|
|
||||||
// // @ts-ignore
|
|
||||||
// toast.error(deletion.data.actionError.message);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// const handleRestore = async () => {
|
|
||||||
// await mutationRestore.mutateAsync();
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// const handleDelete = async () => {
|
|
||||||
// await mutationDeleteBackup.mutateAsync();
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// const handleDownload = async (fileName: string) => {
|
|
||||||
//
|
|
||||||
// let url: string = "";
|
|
||||||
// let data: SafeActionResult<string, ZodString, readonly [], {
|
|
||||||
// _errors?: string[] | undefined;
|
|
||||||
// }, readonly [], ServerActionResult<string>, object> | undefined
|
|
||||||
//
|
|
||||||
// if (settings.storage == "local") {
|
|
||||||
// data = await getFileUrlPresignedLocal({fileName: fileName!})
|
|
||||||
// } else if (settings.storage == "s3") {
|
|
||||||
// data = await getFileUrlPreSignedS3Action(`backups/${database.project?.slug}/${fileName}`);
|
|
||||||
// }
|
|
||||||
// if (data?.data?.success) {
|
|
||||||
// url = data.data.value ?? "";
|
|
||||||
// } else {
|
|
||||||
// // @ts-ignore
|
|
||||||
// const errorMessage = data?.data?.actionError?.message || "Failed to get file!";
|
|
||||||
// toast.error(errorMessage);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// window.open(url, "_self");
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// return (
|
|
||||||
// <>
|
|
||||||
// {(rowData.deletedAt == null && activeMember.role != "member") && (
|
|
||||||
// <DropdownMenu>
|
|
||||||
// <DropdownMenuTrigger asChild>
|
|
||||||
// <Button variant="ghost" className="h-8 w-8 p-0">
|
|
||||||
// <span className="sr-only">Open menu</span>
|
|
||||||
// <MoreHorizontal className="h-4 w-4"/>
|
|
||||||
// </Button>
|
|
||||||
// </DropdownMenuTrigger>
|
|
||||||
// <DropdownMenuContent align="end">
|
|
||||||
// <DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
||||||
// {status == "success" ? (
|
|
||||||
// <>
|
|
||||||
// <TooltipCustom disabled={isAlreadyRestore}
|
|
||||||
// text="Already a restoration waiting">
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// disabled={mutationRestore.isPending || isAlreadyRestore}
|
|
||||||
// onClick={async () => {
|
|
||||||
// await handleRestore();
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// <ReloadIcon/> Restore
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </TooltipCustom>
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// onClick={async () => {
|
|
||||||
// await handleDownload(fileName ?? "");
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// <Download/> Download
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </>
|
|
||||||
// ) : null}
|
|
||||||
// <DropdownMenuSeparator/>
|
|
||||||
// <DropdownMenuItem
|
|
||||||
// className="text-red-600"
|
|
||||||
// onClick={async () => {
|
|
||||||
// await handleDelete();
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// <Trash2/> Delete
|
|
||||||
// </DropdownMenuItem>
|
|
||||||
// </DropdownMenuContent>
|
|
||||||
// </DropdownMenu>
|
|
||||||
// )}
|
|
||||||
// </>
|
|
||||||
//
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
+652
-665
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user