mirror of
https://github.com/ente-io/ente.git
synced 2025-08-08 15:30:40 +00:00
Merge branch 'main' of https://github.com/ente-io/auth into mobile_face
This commit is contained in:
commit
f9dd509d61
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Set line endings of shell scripts to LF, even on Windows, otherwise execution
|
||||||
|
# within Docker fails.
|
||||||
|
*.sh text eol=lf
|
9
.github/workflows/auth-crowdin.yml
vendored
9
.github/workflows/auth-crowdin.yml
vendored
@ -3,15 +3,16 @@ name: "Sync Crowdin translations (auth)"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
# Run action when auth's intl_en.arb is changed
|
# Run workflow when auth's intl_en.arb is changed
|
||||||
- "mobile/lib/l10n/arb/app_en.arb"
|
- "mobile/lib/l10n/arb/app_en.arb"
|
||||||
# Or the workflow itself is changed
|
# Or the workflow itself is changed
|
||||||
- ".github/workflows/auth-crowdin.yml"
|
- ".github/workflows/auth-crowdin.yml"
|
||||||
branches: [main]
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
# Run every 24 hours - https://crontab.guru/#0_*/24_*_*_*
|
# See: [Note: Run every 24 hours]
|
||||||
- cron: "0 */24 * * *"
|
- cron: "50 1 * * *"
|
||||||
workflow_dispatch: # Allow manually running the action
|
# Also allow manually running the workflow
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
|
6
.github/workflows/auth-lint.yml
vendored
6
.github/workflows/auth-lint.yml
vendored
@ -1,11 +1,9 @@
|
|||||||
name: "Lint (auth)"
|
name: "Lint (auth)"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Run on every push to branches (this also covers pull requests)
|
# Run on every push to a branch other than main that changes auth/
|
||||||
push:
|
push:
|
||||||
# See: [Note: Specify branch when specifying a path filter]
|
branches-ignore: [main]
|
||||||
branches: ["**"]
|
|
||||||
# Only run if something changes in these paths
|
|
||||||
paths:
|
paths:
|
||||||
- "auth/**"
|
- "auth/**"
|
||||||
- ".github/workflows/auth-lint.yml"
|
- ".github/workflows/auth-lint.yml"
|
||||||
|
7
.github/workflows/auth-release.yml
vendored
7
.github/workflows/auth-release.yml
vendored
@ -29,7 +29,7 @@ on:
|
|||||||
- "auth-v*"
|
- "auth-v*"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FLUTTER_VERSION: "3.16.9"
|
FLUTTER_VERSION: "3.13.4"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-ubuntu:
|
build-ubuntu:
|
||||||
@ -118,14 +118,11 @@ jobs:
|
|||||||
updateOnlyUnreleased: true
|
updateOnlyUnreleased: true
|
||||||
|
|
||||||
- name: Upload AAB to PlayStore
|
- name: Upload AAB to PlayStore
|
||||||
# Temporarily disable GP upload, enable this once desktop build
|
|
||||||
# testing is complete.
|
|
||||||
if: false
|
|
||||||
uses: r0adkll/upload-google-play@v1
|
uses: r0adkll/upload-google-play@v1
|
||||||
with:
|
with:
|
||||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
||||||
packageName: io.ente.auth
|
packageName: io.ente.auth
|
||||||
releaseFiles: build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
|
releaseFiles: auth/build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
|
||||||
track: internal
|
track: internal
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
|
2
.github/workflows/cli-release.yml
vendored
2
.github/workflows/cli-release.yml
vendored
@ -49,6 +49,6 @@ jobs:
|
|||||||
project_path: "./cli"
|
project_path: "./cli"
|
||||||
pre_command: export CGO_ENABLED=0
|
pre_command: export CGO_ENABLED=0
|
||||||
build_flags: "-trimpath"
|
build_flags: "-trimpath"
|
||||||
ldflags: "-s -w"
|
ldflags: "-X main.AppVersion=${{ github.ref_name }} -s -w"
|
||||||
md5sum: false
|
md5sum: false
|
||||||
sha256sum: true
|
sha256sum: true
|
||||||
|
47
.github/workflows/docs-deploy.yml
vendored
Normal file
47
.github/workflows/docs-deploy.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
name: "Deploy (docs)"
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Run on every push to main that changes docs/
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- "docs/**"
|
||||||
|
- ".github/workflows/docs-deploy.yml"
|
||||||
|
# Also allow manually running the workflow
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: docs
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build production site
|
||||||
|
# Will create docs/.vitepress/dist
|
||||||
|
run: yarn build
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: help
|
||||||
|
directory: docs/docs/.vitepress/dist
|
||||||
|
wranglerVersion: "3"
|
37
.github/workflows/docs-verify-build.yml
vendored
Normal file
37
.github/workflows/docs-verify-build.yml
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
name: "Verify build (docs)"
|
||||||
|
|
||||||
|
# Preflight build of docs. This allows us to ensure that yarn build is
|
||||||
|
# succeeding before we merge the PR into main.
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Run on every push to a branch other than main that changes docs/
|
||||||
|
push:
|
||||||
|
branches-ignore: [main]
|
||||||
|
paths:
|
||||||
|
- "docs/**"
|
||||||
|
- ".github/workflows/docs-verify-build.yml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
verify-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: docs
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build production site
|
||||||
|
run: yarn build
|
9
.github/workflows/mobile-crowdin.yml
vendored
9
.github/workflows/mobile-crowdin.yml
vendored
@ -3,15 +3,16 @@ name: "Sync Crowdin translations (mobile)"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
# Run action when mobiles's intl_en.arb is changed
|
# Run workflow when mobiles's intl_en.arb is changed
|
||||||
- "mobile/lib/l10n/intl_en.arb"
|
- "mobile/lib/l10n/intl_en.arb"
|
||||||
# Or the workflow itself is changed
|
# Or the workflow itself is changed
|
||||||
- ".github/workflows/mobile-crowdin.yml"
|
- ".github/workflows/mobile-crowdin.yml"
|
||||||
branches: [main]
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
# Run every 24 hours - https://crontab.guru/#0_*/24_*_*_*
|
# See: [Note: Run every 24 hours]
|
||||||
- cron: "0 */24 * * *"
|
- cron: "40 1 * * *"
|
||||||
workflow_dispatch: # Allow manually running the action
|
# Also allow manually running the workflow
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
|
6
.github/workflows/mobile-lint.yml
vendored
6
.github/workflows/mobile-lint.yml
vendored
@ -1,11 +1,9 @@
|
|||||||
name: "Lint (mobile)"
|
name: "Lint (mobile)"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Run on every push (this also covers pull requests)
|
# Run on every push to a branch other than main that changes mobile/
|
||||||
push:
|
push:
|
||||||
# See: [Note: Specify branch when specifying a path filter]
|
branches-ignore: [main]
|
||||||
branches: ["**"]
|
|
||||||
# Only run if something changes in these paths
|
|
||||||
paths:
|
paths:
|
||||||
- "mobile/**"
|
- "mobile/**"
|
||||||
- ".github/workflows/mobile-lint.yml"
|
- ".github/workflows/mobile-lint.yml"
|
||||||
|
6
.github/workflows/mobile-release.yml
vendored
6
.github/workflows/mobile-release.yml
vendored
@ -39,7 +39,9 @@ jobs:
|
|||||||
encodedString: ${{ secrets.SIGNING_KEY_PHOTOS }}
|
encodedString: ${{ secrets.SIGNING_KEY_PHOTOS }}
|
||||||
|
|
||||||
- name: Build independent APK
|
- name: Build independent APK
|
||||||
run: flutter build apk --release --flavor independent && mv build/app/outputs/flutter-apk/app-independent-release.apk build/app/outputs/flutter-apk/ente.apk
|
run: |
|
||||||
|
flutter build apk --release --flavor independent
|
||||||
|
mv build/app/outputs/flutter-apk/app-independent-release.apk build/app/outputs/flutter-apk/ente-${{ github.ref_name }}.apk
|
||||||
env:
|
env:
|
||||||
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_photos_key.jks"
|
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_photos_key.jks"
|
||||||
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS_PHOTOS }}
|
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS_PHOTOS }}
|
||||||
@ -52,5 +54,5 @@ jobs:
|
|||||||
- name: Create a draft GitHub release
|
- name: Create a draft GitHub release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
artifacts: "mobile/build/app/outputs/flutter-apk/ente.apk,mobile/build/app/outputs/flutter-apk/sha256sum"
|
artifacts: "mobile/build/app/outputs/flutter-apk/ente-${{ github.ref_name }}.apk,mobile/build/app/outputs/flutter-apk/sha256sum"
|
||||||
draft: true
|
draft: true
|
||||||
|
6
.github/workflows/server-lint.yml
vendored
6
.github/workflows/server-lint.yml
vendored
@ -1,11 +1,9 @@
|
|||||||
name: "Lint (server)"
|
name: "Lint (server)"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Run on every push (this also covers pull requests)
|
# Run on every push to a branch other than main that changes server/
|
||||||
push:
|
push:
|
||||||
# See: [Note: Specify branch when specifying a path filter]
|
branches-ignore: [main]
|
||||||
branches: ["**"]
|
|
||||||
# Only run if something changes in these paths
|
|
||||||
paths:
|
paths:
|
||||||
- "server/**"
|
- "server/**"
|
||||||
- ".github/workflows/server-lint.yml"
|
- ".github/workflows/server-lint.yml"
|
||||||
|
9
.github/workflows/web-crowdin.yml
vendored
9
.github/workflows/web-crowdin.yml
vendored
@ -3,15 +3,16 @@ name: "Sync Crowdin translations (web)"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
# Run action when web's en-US/translation.json is changed
|
# Run workflow when web's en-US/translation.json is changed
|
||||||
- "web/apps/photos/public/locales/en-US/translation.json"
|
- "web/apps/photos/public/locales/en-US/translation.json"
|
||||||
# Or the workflow itself is changed
|
# Or the workflow itself is changed
|
||||||
- ".github/workflows/web-crowdin.yml"
|
- ".github/workflows/web-crowdin.yml"
|
||||||
branches: [main]
|
branches: [main]
|
||||||
schedule:
|
schedule:
|
||||||
# Run every 24 hours - https://crontab.guru/#0_*/24_*_*_*
|
# See: [Note: Run every 24 hours]
|
||||||
- cron: "0 */24 * * *"
|
- cron: "20 1 * * *"
|
||||||
workflow_dispatch: # Allow manually running the action
|
# Also allow manually running the workflow
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
synchronize-with-crowdin:
|
synchronize-with-crowdin:
|
||||||
|
43
.github/workflows/web-deploy-accounts.yml
vendored
Normal file
43
.github/workflows/web-deploy-accounts.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: "Deploy (accounts)"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# Run workflow on pushes to the deploy/accounts
|
||||||
|
branches: [deploy/accounts]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: web
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build accounts
|
||||||
|
run: yarn build:accounts
|
||||||
|
|
||||||
|
- name: Publish accounts
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: deploy/accounts
|
||||||
|
directory: web/apps/accounts/out
|
||||||
|
wranglerVersion: "3"
|
43
.github/workflows/web-deploy-auth.yml
vendored
Normal file
43
.github/workflows/web-deploy-auth.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: "Deploy (auth)"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# Run workflow on pushes to the deploy/auth
|
||||||
|
branches: [deploy/auth]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: web
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build auth
|
||||||
|
run: yarn build:auth
|
||||||
|
|
||||||
|
- name: Publish auth
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: deploy/auth
|
||||||
|
directory: web/apps/auth/out
|
||||||
|
wranglerVersion: "3"
|
43
.github/workflows/web-deploy-cast.yml
vendored
Normal file
43
.github/workflows/web-deploy-cast.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: "Deploy (cast)"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# Run workflow on pushes to the deploy/cast
|
||||||
|
branches: [deploy/cast]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: web
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build cast
|
||||||
|
run: yarn build:cast
|
||||||
|
|
||||||
|
- name: Publish cast
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: deploy/cast
|
||||||
|
directory: web/apps/cast/out
|
||||||
|
wranglerVersion: "3"
|
43
.github/workflows/web-deploy-photos.yml
vendored
Normal file
43
.github/workflows/web-deploy-photos.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: "Deploy (photos)"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# Run workflow on pushes to the deploy/photos
|
||||||
|
branches: [deploy/photos]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: web
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build photos
|
||||||
|
run: yarn build:photos
|
||||||
|
|
||||||
|
- name: Publish photos
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: deploy/photos
|
||||||
|
directory: web/apps/photos/out
|
||||||
|
wranglerVersion: "3"
|
15
.github/workflows/web-lint.yml
vendored
15
.github/workflows/web-lint.yml
vendored
@ -1,20 +1,9 @@
|
|||||||
name: "Lint (web)"
|
name: "Lint (web)"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# Run on every push (this also covers pull requests)
|
# Run on every push to a branch other than main that changes web/
|
||||||
push:
|
push:
|
||||||
# [Note: Specify branch when specifying a path filter]
|
branches-ignore: [main]
|
||||||
#
|
|
||||||
# Path filters are ignored for tag pushes, which causes this workflow to
|
|
||||||
# always run when we push a tag. Defining an explicit branch solves the
|
|
||||||
# issue. From GitHub's docs:
|
|
||||||
#
|
|
||||||
# > if you define both branches/branches-ignore and paths/paths-ignore,
|
|
||||||
# > the workflow will only run when both filters are satisfied.
|
|
||||||
#
|
|
||||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
|
|
||||||
branches: ["**"]
|
|
||||||
# Only run if something changes in these paths
|
|
||||||
paths:
|
paths:
|
||||||
- "web/**"
|
- "web/**"
|
||||||
- ".github/workflows/web-lint.yml"
|
- ".github/workflows/web-lint.yml"
|
||||||
|
94
.github/workflows/web-nightly.yml
vendored
Normal file
94
.github/workflows/web-nightly.yml
vendored
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
name: "Nightly (web)"
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# [Note: Run every 24 hours]
|
||||||
|
#
|
||||||
|
# Run every 24 hours - First field is minute, second is hour of the day
|
||||||
|
# This runs 23:15 UTC everyday - 1 and 15 are just arbitrary offset to
|
||||||
|
# avoid scheduling it on the exact hour, as suggested by GitHub.
|
||||||
|
#
|
||||||
|
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
|
||||||
|
# https://crontab.guru/
|
||||||
|
#
|
||||||
|
- cron: "15 23 * * *"
|
||||||
|
# Also allow manually running the workflow
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: web
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build accounts
|
||||||
|
run: yarn build:accounts
|
||||||
|
|
||||||
|
- name: Publish accounts
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: n-accounts
|
||||||
|
directory: web/apps/accounts/out
|
||||||
|
wranglerVersion: "3"
|
||||||
|
|
||||||
|
- name: Build auth
|
||||||
|
run: yarn build:auth
|
||||||
|
|
||||||
|
- name: Publish auth
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: n-auth
|
||||||
|
directory: web/apps/auth/out
|
||||||
|
wranglerVersion: "3"
|
||||||
|
|
||||||
|
- name: Build cast
|
||||||
|
run: yarn build:cast
|
||||||
|
|
||||||
|
- name: Publish cast
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: n-cast
|
||||||
|
directory: web/apps/cast/out
|
||||||
|
wranglerVersion: "3"
|
||||||
|
|
||||||
|
- name: Build photos
|
||||||
|
run: yarn build:photos
|
||||||
|
env:
|
||||||
|
NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT: https://albums.ente.sh
|
||||||
|
|
||||||
|
- name: Publish photos
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: n-photos
|
||||||
|
directory: web/apps/photos/out
|
||||||
|
wranglerVersion: "3"
|
52
.github/workflows/web-preview.yml
vendored
Normal file
52
.github/workflows/web-preview.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
name: "Preview (web)"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
app:
|
||||||
|
description: "App to build and deploy"
|
||||||
|
type: choice
|
||||||
|
required: true
|
||||||
|
default: "photos"
|
||||||
|
options:
|
||||||
|
- "accounts"
|
||||||
|
- "auth"
|
||||||
|
- "cast"
|
||||||
|
- "photos"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: web
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Setup node and enable yarn caching
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: "yarn"
|
||||||
|
cache-dependency-path: "docs/yarn.lock"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Build ${{ inputs.app }}
|
||||||
|
run: yarn build:${{ inputs.app }}
|
||||||
|
|
||||||
|
- name: Publish ${{ inputs.app }} to preview
|
||||||
|
uses: cloudflare/pages-action@1
|
||||||
|
with:
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
projectName: ente
|
||||||
|
branch: preview
|
||||||
|
directory: web/apps/${{ inputs.app }}/out
|
||||||
|
wranglerVersion: "3"
|
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -9,10 +9,6 @@
|
|||||||
[submodule "auth/assets/simple-icons"]
|
[submodule "auth/assets/simple-icons"]
|
||||||
path = auth/assets/simple-icons
|
path = auth/assets/simple-icons
|
||||||
url = https://github.com/simple-icons/simple-icons.git
|
url = https://github.com/simple-icons/simple-icons.git
|
||||||
[submodule "desktop/thirdparty/next-electron-server"]
|
|
||||||
path = desktop/thirdparty/next-electron-server
|
|
||||||
url = https://github.com/ente-io/next-electron-server.git
|
|
||||||
branch = desktop
|
|
||||||
[submodule "mobile/thirdparty/flutter"]
|
[submodule "mobile/thirdparty/flutter"]
|
||||||
path = mobile/thirdparty/flutter
|
path = mobile/thirdparty/flutter
|
||||||
url = https://github.com/flutter/flutter.git
|
url = https://github.com/flutter/flutter.git
|
||||||
|
@ -50,13 +50,13 @@ Thank you for your support.
|
|||||||
|
|
||||||
## Document
|
## Document
|
||||||
|
|
||||||
_Coming soon!_
|
|
||||||
|
|
||||||
The help guides and FAQs for users of Ente products are also open source, and
|
The help guides and FAQs for users of Ente products are also open source, and
|
||||||
can be edited in a wiki-esque manner by our community members. More than the
|
can be edited in a wiki-esque manner by our community members. More than the
|
||||||
quantity, we feel this helps improve the quality and approachability of the
|
quantity, we feel this helps improve the quality and approachability of the
|
||||||
documentation by bringing in more diverse viewpoints and familiarity levels.
|
documentation by bringing in more diverse viewpoints and familiarity levels.
|
||||||
|
|
||||||
|
See [docs/](docs/README.md) for how to edit these documents.
|
||||||
|
|
||||||
## Code contributions
|
## Code contributions
|
||||||
|
|
||||||
If you'd like to contribute code, it is best to start small.
|
If you'd like to contribute code, it is best to start small.
|
||||||
|
@ -70,7 +70,7 @@ existing users will be grandfathered in.
|
|||||||
[<img height="42" src=".github/assets/app-store-badge.svg">](https://apps.apple.com/app/id6444121398)
|
[<img height="42" src=".github/assets/app-store-badge.svg">](https://apps.apple.com/app/id6444121398)
|
||||||
[<img height="42" src=".github/assets/play-store-badge.png">](https://play.google.com/store/apps/details?id=io.ente.auth)
|
[<img height="42" src=".github/assets/play-store-badge.png">](https://play.google.com/store/apps/details?id=io.ente.auth)
|
||||||
[<img height="42" src=".github/assets/f-droid-badge.png">](https://f-droid.org/packages/io.ente.auth/)
|
[<img height="42" src=".github/assets/f-droid-badge.png">](https://f-droid.org/packages/io.ente.auth/)
|
||||||
[<img height="42" src=".github/assets/github-badge.png">](https://github.com/ente-io/ente/releases?q=tag%3Av2.0.34&expanded=true)
|
[<img height="42" src=".github/assets/github-badge.png">](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v2)
|
||||||
[<img height="42" src=".github/assets/web-badge.svg">](https://auth.ente.io)
|
[<img height="42" src=".github/assets/web-badge.svg">](https://auth.ente.io)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ details as possible about whatever it is that you need help with, and we will
|
|||||||
get back to you as soon as possible.
|
get back to you as soon as possible.
|
||||||
|
|
||||||
In some cases, your query might already have been answered in our help
|
In some cases, your query might already have been answered in our help
|
||||||
documentation (_Coming soon!_).
|
documentation at [help.ente.io](https://help.ente.io).
|
||||||
|
|
||||||
Other ways to get in touch are:
|
Other ways to get in touch are:
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ multi-device sync.
|
|||||||
### Android
|
### Android
|
||||||
|
|
||||||
This repository's [GitHub
|
This repository's [GitHub
|
||||||
releases](https://github.com/ente-io/ente/releases/latest/download/ente-auth.apk)
|
releases](https://github.com/ente-io/ente/releases?q=tag%3Aauth-v2)
|
||||||
contains APKs, built straight from source. These builds keep themselves updated,
|
contains APKs, built straight from source. These builds keep themselves updated,
|
||||||
without relying on third party stores.
|
without relying on third party stores.
|
||||||
|
|
||||||
|
@ -37,8 +37,7 @@
|
|||||||
{
|
{
|
||||||
"title": "BorgBase",
|
"title": "BorgBase",
|
||||||
"altNames": ["borg"],
|
"altNames": ["borg"],
|
||||||
"slug": "BorgBase",
|
"slug": "BorgBase"
|
||||||
"hex": "222C31"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Brave Creators",
|
"title": "Brave Creators",
|
||||||
@ -109,8 +108,7 @@
|
|||||||
{
|
{
|
||||||
"title": "Gosuslugi",
|
"title": "Gosuslugi",
|
||||||
"altNames": ["Госуслуги"],
|
"altNames": ["Госуслуги"],
|
||||||
"slug": "Gosuslugi",
|
"slug": "Gosuslugi"
|
||||||
"hex": "EE2F53"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Healthchecks.io",
|
"title": "Healthchecks.io",
|
||||||
@ -127,13 +125,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "IVPN",
|
"title": "IVPN",
|
||||||
"slug": "IVPN",
|
"slug": "IVPN"
|
||||||
"hex": "FA3243"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "IceDrive",
|
"title": "IceDrive",
|
||||||
"slug": "Icedrive",
|
"slug": "Icedrive"
|
||||||
"hex": "1F4FD0"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Jagex",
|
"title": "Jagex",
|
||||||
@ -154,8 +150,7 @@
|
|||||||
"title": "Kite"
|
"title": "Kite"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Koofr",
|
"title": "Koofr"
|
||||||
"hex": "71BA05"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Kraken",
|
"title": "Kraken",
|
||||||
@ -184,8 +179,7 @@
|
|||||||
{
|
{
|
||||||
"title": "Murena",
|
"title": "Murena",
|
||||||
"altNames": ["eCloud"],
|
"altNames": ["eCloud"],
|
||||||
"slug": "ecloud",
|
"slug": "ecloud"
|
||||||
"hex": "EC6A55"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Microsoft"
|
"title": "Microsoft"
|
||||||
@ -230,8 +224,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "pCloud",
|
"title": "pCloud",
|
||||||
"slug": "pCloud",
|
"slug": "pCloud"
|
||||||
"hex": "1EBCC5"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Peerberry",
|
"title": "Peerberry",
|
||||||
@ -371,8 +364,7 @@
|
|||||||
{
|
{
|
||||||
"title": "Yandex",
|
"title": "Yandex",
|
||||||
"altNames": ["Ya", "Яндекс"],
|
"altNames": ["Ya", "Яндекс"],
|
||||||
"slug": "Yandex",
|
"slug": "Yandex"
|
||||||
"hex": "FC3F1D"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -71,8 +71,6 @@ PODS:
|
|||||||
- move_to_background (0.0.1):
|
- move_to_background (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- MTBBarcodeScanner (5.0.11)
|
- MTBBarcodeScanner (5.0.11)
|
||||||
- open_filex (0.0.2):
|
|
||||||
- Flutter
|
|
||||||
- OrderedSet (5.0.0)
|
- OrderedSet (5.0.0)
|
||||||
- package_info_plus (0.4.5):
|
- package_info_plus (0.4.5):
|
||||||
- Flutter
|
- Flutter
|
||||||
@ -126,7 +124,6 @@ DEPENDENCIES:
|
|||||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||||
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
|
- local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
|
||||||
- move_to_background (from `.symlinks/plugins/move_to_background/ios`)
|
- move_to_background (from `.symlinks/plugins/move_to_background/ios`)
|
||||||
- open_filex (from `.symlinks/plugins/open_filex/ios`)
|
|
||||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- privacy_screen (from `.symlinks/plugins/privacy_screen/ios`)
|
- privacy_screen (from `.symlinks/plugins/privacy_screen/ios`)
|
||||||
@ -183,8 +180,6 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/local_auth_ios/ios"
|
:path: ".symlinks/plugins/local_auth_ios/ios"
|
||||||
move_to_background:
|
move_to_background:
|
||||||
:path: ".symlinks/plugins/move_to_background/ios"
|
:path: ".symlinks/plugins/move_to_background/ios"
|
||||||
open_filex:
|
|
||||||
:path: ".symlinks/plugins/open_filex/ios"
|
|
||||||
package_info_plus:
|
package_info_plus:
|
||||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
@ -214,7 +209,7 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
|
file_picker: ce3938a0df3cc1ef404671531facef740d03f920
|
||||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||||
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
|
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||||
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b
|
||||||
flutter_inappwebview: acd4fc0f012cefd09015000c241137d82f01ba62
|
flutter_inappwebview: acd4fc0f012cefd09015000c241137d82f01ba62
|
||||||
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743
|
||||||
@ -226,7 +221,6 @@ SPEC CHECKSUMS:
|
|||||||
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
|
local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
|
||||||
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
||||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||||
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
|
|
||||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||||
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
|
||||||
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||||
|
@ -167,7 +167,7 @@ class SuperLogging {
|
|||||||
await setupLogDir();
|
await setupLogDir();
|
||||||
}
|
}
|
||||||
if (sentryIsEnabled) {
|
if (sentryIsEnabled) {
|
||||||
await setupSentry();
|
setupSentry().ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.root.level = Level.ALL;
|
Logger.root.level = Level.ALL;
|
||||||
@ -250,7 +250,7 @@ class SuperLogging {
|
|||||||
|
|
||||||
// add error to sentry queue
|
// add error to sentry queue
|
||||||
if (sentryIsEnabled && rec.error != null) {
|
if (sentryIsEnabled && rec.error != null) {
|
||||||
await _sendErrorToSentry(rec.error!, null);
|
_sendErrorToSentry(rec.error!, null).ignore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +144,8 @@
|
|||||||
"enterCodeHint": "Enter the 6-digit code from\nyour authenticator app",
|
"enterCodeHint": "Enter the 6-digit code from\nyour authenticator app",
|
||||||
"lostDeviceTitle": "Lost device?",
|
"lostDeviceTitle": "Lost device?",
|
||||||
"twoFactorAuthTitle": "Two-factor authentication",
|
"twoFactorAuthTitle": "Two-factor authentication",
|
||||||
"passkeyAuthTitle": "Passkey authentication",
|
"passkeyAuthTitle": "Passkey verification",
|
||||||
|
"verifyPasskey": "Verify passkey",
|
||||||
"recoverAccount": "Recover account",
|
"recoverAccount": "Recover account",
|
||||||
"enterRecoveryKeyHint": "Enter your recovery key",
|
"enterRecoveryKeyHint": "Enter your recovery key",
|
||||||
"recover": "Recover",
|
"recover": "Recover",
|
||||||
@ -407,7 +408,7 @@
|
|||||||
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
|
"hearUsWhereTitle": "How did you hear about Ente? (optional)",
|
||||||
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!",
|
"hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!",
|
||||||
"waitingForBrowserRequest": "Waiting for browser request...",
|
"waitingForBrowserRequest": "Waiting for browser request...",
|
||||||
"launchPasskeyUrlAgain": "Launch passkey URL again",
|
"waitingForVerification": "Waiting for verification...",
|
||||||
"passkey": "Passkey",
|
"passkey": "Passkey",
|
||||||
"developerSettingsWarning":"Are you sure that you want to modify Developer settings?",
|
"developerSettingsWarning":"Are you sure that you want to modify Developer settings?",
|
||||||
"developerSettings": "Developer settings",
|
"developerSettings": "Developer settings",
|
||||||
|
@ -59,6 +59,12 @@
|
|||||||
"recreatePassword": "Återskapa lösenord",
|
"recreatePassword": "Återskapa lösenord",
|
||||||
"useRecoveryKey": "Använd återställningsnyckel",
|
"useRecoveryKey": "Använd återställningsnyckel",
|
||||||
"incorrectPasswordTitle": "Felaktigt lösenord",
|
"incorrectPasswordTitle": "Felaktigt lösenord",
|
||||||
|
"welcomeBack": "Välkommen tillbaka!",
|
||||||
|
"changePassword": "Ändra lösenord",
|
||||||
|
"cancel": "Avbryt",
|
||||||
|
"yes": "Ja",
|
||||||
|
"no": "Nej",
|
||||||
|
"settings": "Inställningar",
|
||||||
"pleaseTryAgain": "Försök igen",
|
"pleaseTryAgain": "Försök igen",
|
||||||
"existingUser": "Befintlig användare",
|
"existingUser": "Befintlig användare",
|
||||||
"delete": "Radera",
|
"delete": "Radera",
|
||||||
@ -68,9 +74,23 @@
|
|||||||
"suggestFeatures": "Föreslå funktionalitet",
|
"suggestFeatures": "Föreslå funktionalitet",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
"faq_q_1": "Hur säkert är ente Auth?",
|
"faq_q_1": "Hur säkert är ente Auth?",
|
||||||
|
"scan": "Skanna",
|
||||||
|
"twoFactorAuthTitle": "Tvåfaktorsautentisering",
|
||||||
|
"enterRecoveryKeyHint": "Ange din återställningsnyckel",
|
||||||
|
"noRecoveryKeyTitle": "Ingen återställningsnyckel?",
|
||||||
|
"enterEmailHint": "Ange din e-postadress",
|
||||||
|
"invalidEmailTitle": "Ogiltig e-postadress",
|
||||||
|
"invalidEmailMessage": "Ange en giltig e-postadress.",
|
||||||
|
"deleteAccount": "Radera konto",
|
||||||
|
"yesSendFeedbackAction": "Ja, skicka feedback",
|
||||||
|
"noDeleteAccountAction": "Nej, radera konto",
|
||||||
|
"createNewAccount": "Skapa nytt konto",
|
||||||
"weakStrength": "Svag",
|
"weakStrength": "Svag",
|
||||||
"strongStrength": "Stark",
|
"strongStrength": "Stark",
|
||||||
"moderateStrength": "Måttligt",
|
"moderateStrength": "Måttligt",
|
||||||
|
"confirmPassword": "Bekräfta lösenord",
|
||||||
|
"close": "Stäng",
|
||||||
|
"language": "Språk",
|
||||||
"searchHint": "Sök...",
|
"searchHint": "Sök...",
|
||||||
"search": "Sök",
|
"search": "Sök",
|
||||||
"sorryUnableToGenCode": "Tyvärr, det gick inte att generera en kod för {issuerName}",
|
"sorryUnableToGenCode": "Tyvärr, det gick inte att generera en kod för {issuerName}",
|
||||||
@ -83,5 +103,45 @@
|
|||||||
"copiedNextToClipboard": "Kopierade nästa kod till urklipp",
|
"copiedNextToClipboard": "Kopierade nästa kod till urklipp",
|
||||||
"error": "Fel",
|
"error": "Fel",
|
||||||
"recoveryKeyCopiedToClipboard": "Återställningsnyckel kopierad till urklipp",
|
"recoveryKeyCopiedToClipboard": "Återställningsnyckel kopierad till urklipp",
|
||||||
"recoveryKeyOnForgotPassword": "Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel."
|
"recoveryKeyOnForgotPassword": "Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.",
|
||||||
|
"saveKey": "Spara nyckel",
|
||||||
|
"back": "Tillbaka",
|
||||||
|
"createAccount": "Skapa konto",
|
||||||
|
"password": "Lösenord",
|
||||||
|
"privacyPolicyTitle": "Integritetspolicy",
|
||||||
|
"termsOfServicesTitle": "Villkor",
|
||||||
|
"encryption": "Kryptering",
|
||||||
|
"changePasswordTitle": "Ändra lösenord",
|
||||||
|
"resetPasswordTitle": "Återställ lösenord",
|
||||||
|
"encryptionKeys": "Krypteringsnycklar",
|
||||||
|
"continueLabel": "Fortsätt",
|
||||||
|
"logInLabel": "Logga in",
|
||||||
|
"logout": "Logga ut",
|
||||||
|
"areYouSureYouWantToLogout": "Är du säker på att du vill logga ut?",
|
||||||
|
"yesLogout": "Ja, logga ut",
|
||||||
|
"invalidKey": "Ogiltig nyckel",
|
||||||
|
"tryAgain": "Försök igen",
|
||||||
|
"viewRecoveryKey": "Visa återställningsnyckel",
|
||||||
|
"confirmRecoveryKey": "Bekräfta återställningsnyckel",
|
||||||
|
"confirmYourRecoveryKey": "Bekräfta din återställningsnyckel",
|
||||||
|
"confirm": "Bekräfta",
|
||||||
|
"copyEmailAddress": "Kopiera e-postadress",
|
||||||
|
"exportLogs": "Exportera loggar",
|
||||||
|
"enterYourRecoveryKey": "Ange din återställningsnyckel",
|
||||||
|
"about": "Om",
|
||||||
|
"terms": "Villkor",
|
||||||
|
"warning": "Varning",
|
||||||
|
"pendingSyncs": "Varning",
|
||||||
|
"activeSessions": "Aktiva sessioner",
|
||||||
|
"enterPassword": "Ange lösenord",
|
||||||
|
"export": "Exportera",
|
||||||
|
"singIn": "Logga in",
|
||||||
|
"androidCancelButton": "Avbryt",
|
||||||
|
"@androidCancelButton": {
|
||||||
|
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
|
||||||
|
},
|
||||||
|
"iOSOkButton": "OK",
|
||||||
|
"@iOSOkButton": {
|
||||||
|
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
||||||
|
}
|
||||||
}
|
}
|
@ -144,6 +144,7 @@
|
|||||||
"enterCodeHint": "从你的身份验证器应用中\n输入6位数字代码",
|
"enterCodeHint": "从你的身份验证器应用中\n输入6位数字代码",
|
||||||
"lostDeviceTitle": "丢失了设备吗?",
|
"lostDeviceTitle": "丢失了设备吗?",
|
||||||
"twoFactorAuthTitle": "双因素认证",
|
"twoFactorAuthTitle": "双因素认证",
|
||||||
|
"passkeyAuthTitle": "通行密钥认证",
|
||||||
"recoverAccount": "恢复账户",
|
"recoverAccount": "恢复账户",
|
||||||
"enterRecoveryKeyHint": "输入您的恢复密钥",
|
"enterRecoveryKeyHint": "输入您的恢复密钥",
|
||||||
"recover": "恢复",
|
"recover": "恢复",
|
||||||
@ -404,5 +405,8 @@
|
|||||||
"signOutOtherDevices": "登出其他设备",
|
"signOutOtherDevices": "登出其他设备",
|
||||||
"doNotSignOut": "不要退登",
|
"doNotSignOut": "不要退登",
|
||||||
"hearUsWhereTitle": "您是如何知道Ente的? (可选的)",
|
"hearUsWhereTitle": "您是如何知道Ente的? (可选的)",
|
||||||
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!"
|
"hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!",
|
||||||
|
"waitingForBrowserRequest": "正在等待浏览器请求...",
|
||||||
|
"launchPasskeyUrlAgain": "再次启动 通行密钥 URL",
|
||||||
|
"passkey": "通行密钥"
|
||||||
}
|
}
|
13
auth/lib/models/account/two_factor.dart
Normal file
13
auth/lib/models/account/two_factor.dart
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
enum TwoFactorType { totp, passkey }
|
||||||
|
|
||||||
|
// ToString for TwoFactorType
|
||||||
|
String twoFactorTypeToString(TwoFactorType type) {
|
||||||
|
switch (type) {
|
||||||
|
case TwoFactorType.totp:
|
||||||
|
return "totp";
|
||||||
|
case TwoFactorType.passkey:
|
||||||
|
return "passkey";
|
||||||
|
default:
|
||||||
|
return type.name;
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,28 @@ class PasskeyService {
|
|||||||
return response.data!["accountsToken"] as String;
|
return response.data!["accountsToken"] as String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isPasskeyRecoveryEnabled() async {
|
||||||
|
final response = await _enteDio.get(
|
||||||
|
"/users/two-factor/recovery-status",
|
||||||
|
);
|
||||||
|
return response.data!["isPasskeyRecoveryEnabled"] as bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> configurePasskeyRecovery(
|
||||||
|
String secret,
|
||||||
|
String userEncryptedSecret,
|
||||||
|
String userSecretNonce,
|
||||||
|
) async {
|
||||||
|
await _enteDio.post(
|
||||||
|
"/users/two-factor/passkeys/configure-recovery",
|
||||||
|
data: {
|
||||||
|
"secret": secret,
|
||||||
|
"userSecretCipher": userEncryptedSecret,
|
||||||
|
"userSecretNonce": userSecretNonce,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> openPasskeyPage(BuildContext context) async {
|
Future<void> openPasskeyPage(BuildContext context) async {
|
||||||
try {
|
try {
|
||||||
final jwtToken = await getJwtToken();
|
final jwtToken = await getJwtToken();
|
||||||
|
@ -11,6 +11,7 @@ import 'package:ente_auth/core/event_bus.dart';
|
|||||||
import 'package:ente_auth/core/network.dart';
|
import 'package:ente_auth/core/network.dart';
|
||||||
import 'package:ente_auth/events/user_details_changed_event.dart';
|
import 'package:ente_auth/events/user_details_changed_event.dart';
|
||||||
import 'package:ente_auth/l10n/l10n.dart';
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
|
import 'package:ente_auth/models/account/two_factor.dart';
|
||||||
import 'package:ente_auth/models/api/user/srp.dart';
|
import 'package:ente_auth/models/api/user/srp.dart';
|
||||||
import 'package:ente_auth/models/delete_account.dart';
|
import 'package:ente_auth/models/delete_account.dart';
|
||||||
import 'package:ente_auth/models/key_attributes.dart';
|
import 'package:ente_auth/models/key_attributes.dart';
|
||||||
@ -762,7 +763,11 @@ class UserService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> recoverTwoFactor(BuildContext context, String sessionID) async {
|
Future<void> recoverTwoFactor(
|
||||||
|
BuildContext context,
|
||||||
|
String sessionID,
|
||||||
|
TwoFactorType type,
|
||||||
|
) async {
|
||||||
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
|
final dialog = createProgressDialog(context, context.l10n.pleaseWait);
|
||||||
await dialog.show();
|
await dialog.show();
|
||||||
try {
|
try {
|
||||||
@ -770,6 +775,7 @@ class UserService {
|
|||||||
_config.getHttpEndpoint() + "/users/two-factor/recover",
|
_config.getHttpEndpoint() + "/users/two-factor/recover",
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
"sessionID": sessionID,
|
"sessionID": sessionID,
|
||||||
|
"twoFactorType": twoFactorTypeToString(type),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
@ -778,6 +784,7 @@ class UserService {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return TwoFactorRecoveryPage(
|
return TwoFactorRecoveryPage(
|
||||||
|
type,
|
||||||
sessionID,
|
sessionID,
|
||||||
response.data["encryptedSecret"],
|
response.data["encryptedSecret"],
|
||||||
response.data["secretDecryptionNonce"],
|
response.data["secretDecryptionNonce"],
|
||||||
@ -788,6 +795,7 @@ class UserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
await dialog.hide();
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
if (e.response != null && e.response!.statusCode == 404) {
|
if (e.response != null && e.response!.statusCode == 404) {
|
||||||
showToast(context, context.l10n.sessionExpired);
|
showToast(context, context.l10n.sessionExpired);
|
||||||
@ -809,6 +817,7 @@ class UserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await dialog.hide();
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
showErrorDialog(
|
showErrorDialog(
|
||||||
@ -823,6 +832,7 @@ class UserService {
|
|||||||
|
|
||||||
Future<void> removeTwoFactor(
|
Future<void> removeTwoFactor(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
|
TwoFactorType type,
|
||||||
String sessionID,
|
String sessionID,
|
||||||
String recoveryKey,
|
String recoveryKey,
|
||||||
String encryptedSecret,
|
String encryptedSecret,
|
||||||
@ -862,6 +872,7 @@ class UserService {
|
|||||||
data: {
|
data: {
|
||||||
"sessionID": sessionID,
|
"sessionID": sessionID,
|
||||||
"secret": secret,
|
"secret": secret,
|
||||||
|
"twoFactorType": twoFactorTypeToString(type),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
@ -881,6 +892,7 @@ class UserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
await dialog.hide();
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
if (e.response != null && e.response!.statusCode == 404) {
|
if (e.response != null && e.response!.statusCode == 404) {
|
||||||
showToast(context, "Session expired");
|
showToast(context, "Session expired");
|
||||||
@ -902,6 +914,7 @@ class UserService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await dialog.hide();
|
||||||
_logger.severe(e);
|
_logger.severe(e);
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
showErrorDialog(
|
showErrorDialog(
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
import 'package:ente_auth/core/configuration.dart';
|
||||||
import 'package:ente_auth/ente_theme_data.dart';
|
|
||||||
import 'package:ente_auth/l10n/l10n.dart';
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
|
import 'package:ente_auth/models/account/two_factor.dart';
|
||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
|
import 'package:ente_auth/ui/components/buttons/button_widget.dart';
|
||||||
|
import 'package:ente_auth/ui/components/models/button_type.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@ -99,30 +101,50 @@ class _PasskeyPageState extends State<PasskeyPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _getBody() {
|
Widget _getBody() {
|
||||||
final l10n = context.l10n;
|
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
children: [
|
child: Column(
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
l10n.waitingForBrowserRequest,
|
children: [
|
||||||
style: const TextStyle(
|
Text(
|
||||||
height: 1.4,
|
context.l10n.waitingForVerification,
|
||||||
fontSize: 16,
|
style: const TextStyle(
|
||||||
|
height: 1.4,
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
const SizedBox(height: 16),
|
ButtonWidget(
|
||||||
Container(
|
buttonType: ButtonType.primary,
|
||||||
width: double.infinity,
|
labelText: context.l10n.verifyPasskey,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
onTap: () => launchPasskey(),
|
||||||
child: ElevatedButton(
|
|
||||||
style: Theme.of(context).colorScheme.optionalActionButtonStyle,
|
|
||||||
onPressed: launchPasskey,
|
|
||||||
child: Text(l10n.launchPasskeyUrlAgain),
|
|
||||||
),
|
),
|
||||||
),
|
const Padding(padding: EdgeInsets.all(30)),
|
||||||
],
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
UserService.instance.recoverTwoFactor(
|
||||||
|
context,
|
||||||
|
widget.sessionID,
|
||||||
|
TwoFactorType.passkey,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(10),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
context.l10n.recoverAccount,
|
||||||
|
style: const TextStyle(
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:ente_auth/core/configuration.dart';
|
|
||||||
import 'package:ente_auth/core/network.dart';
|
|
||||||
import 'package:ente_auth/ente_theme_data.dart';
|
|
||||||
import 'package:ente_auth/l10n/l10n.dart';
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
import 'package:ente_auth/services/update_service.dart';
|
import 'package:ente_auth/services/update_service.dart';
|
||||||
import 'package:ente_auth/theme/ente_theme.dart';
|
import 'package:ente_auth/theme/ente_theme.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:open_filex/open_filex.dart';
|
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class AppUpdateDialog extends StatefulWidget {
|
class AppUpdateDialog extends StatefulWidget {
|
||||||
@ -114,116 +107,3 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApkDownloaderDialog extends StatefulWidget {
|
|
||||||
final LatestVersionInfo? versionInfo;
|
|
||||||
|
|
||||||
const ApkDownloaderDialog(this.versionInfo, {Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<ApkDownloaderDialog> createState() => _ApkDownloaderDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ApkDownloaderDialogState extends State<ApkDownloaderDialog> {
|
|
||||||
String? _saveUrl;
|
|
||||||
double? _downloadProgress;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_saveUrl = Configuration.instance.getTempDirectory() +
|
|
||||||
"ente-" +
|
|
||||||
widget.versionInfo!.name! +
|
|
||||||
".apk";
|
|
||||||
_downloadApk();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return WillPopScope(
|
|
||||||
onWillPop: () async => false,
|
|
||||||
child: AlertDialog(
|
|
||||||
title: const Text(
|
|
||||||
"Downloading...",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
content: LinearProgressIndicator(
|
|
||||||
value: _downloadProgress,
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
|
||||||
Theme.of(context).colorScheme.alternativeColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _downloadApk() async {
|
|
||||||
try {
|
|
||||||
if (!File(_saveUrl!).existsSync()) {
|
|
||||||
await Network.instance.getDio().download(
|
|
||||||
widget.versionInfo!.url!,
|
|
||||||
_saveUrl,
|
|
||||||
onReceiveProgress: (count, _) {
|
|
||||||
setState(() {
|
|
||||||
_downloadProgress = count / widget.versionInfo!.size!;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
|
||||||
// ignore: unawaited_futures
|
|
||||||
OpenFilex.open(_saveUrl);
|
|
||||||
} catch (e) {
|
|
||||||
Logger("ApkDownloader").severe(e);
|
|
||||||
final AlertDialog alert = AlertDialog(
|
|
||||||
title: const Text("Sorry"),
|
|
||||||
content: const Text("The download could not be completed"),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
child: const Text(
|
|
||||||
"Ignore",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
|
||||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
child: Text(
|
|
||||||
"Retry",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.alternativeColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
|
||||||
Navigator.of(context, rootNavigator: true).pop('dialog');
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return ApkDownloaderDialog(widget.versionInfo);
|
|
||||||
},
|
|
||||||
barrierDismissible: false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// ignore: unawaited_futures
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return alert;
|
|
||||||
},
|
|
||||||
barrierColor: Colors.black87,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -21,6 +21,7 @@ import 'package:ente_auth/utils/dialog_util.dart';
|
|||||||
import 'package:ente_auth/utils/navigation_util.dart';
|
import 'package:ente_auth/utils/navigation_util.dart';
|
||||||
import 'package:ente_auth/utils/toast_util.dart';
|
import 'package:ente_auth/utils/toast_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
class SecuritySectionWidget extends StatefulWidget {
|
class SecuritySectionWidget extends StatefulWidget {
|
||||||
const SecuritySectionWidget({Key? key}) : super(key: key);
|
const SecuritySectionWidget({Key? key}) : super(key: key);
|
||||||
@ -32,6 +33,7 @@ class SecuritySectionWidget extends StatefulWidget {
|
|||||||
class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
||||||
final _config = Configuration.instance;
|
final _config = Configuration.instance;
|
||||||
late bool _hasLoggedIn;
|
late bool _hasLoggedIn;
|
||||||
|
final Logger _logger = Logger('SecuritySectionWidget');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -75,7 +77,7 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||||
trailingIcon: Icons.chevron_right_outlined,
|
trailingIcon: Icons.chevron_right_outlined,
|
||||||
trailingIconIsMuted: true,
|
trailingIconIsMuted: true,
|
||||||
onTap: () => PasskeyService.instance.openPasskeyPage(context),
|
onTap: () async => await onPasskeyClick(context),
|
||||||
),
|
),
|
||||||
sectionOptionSpacing,
|
sectionOptionSpacing,
|
||||||
MenuItemWidget(
|
MenuItemWidget(
|
||||||
@ -159,6 +161,31 @@ class _SecuritySectionWidgetState extends State<SecuritySectionWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> onPasskeyClick(BuildContext buildContext) async {
|
||||||
|
try {
|
||||||
|
final isPassKeyResetEnabled =
|
||||||
|
await PasskeyService.instance.isPasskeyRecoveryEnabled();
|
||||||
|
if (!isPassKeyResetEnabled) {
|
||||||
|
final Uint8List recoveryKey = Configuration.instance.getRecoveryKey();
|
||||||
|
final resetKey = CryptoUtil.generateKey();
|
||||||
|
final resetKeyBase64 = CryptoUtil.bin2base64(resetKey);
|
||||||
|
final encryptionResult = CryptoUtil.encryptSync(
|
||||||
|
resetKey,
|
||||||
|
recoveryKey,
|
||||||
|
);
|
||||||
|
await PasskeyService.instance.configurePasskeyRecovery(
|
||||||
|
resetKeyBase64,
|
||||||
|
CryptoUtil.bin2base64(encryptionResult.encryptedData!),
|
||||||
|
CryptoUtil.bin2base64(encryptionResult.nonce!),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
PasskeyService.instance.openPasskeyPage(buildContext).ignore();
|
||||||
|
} catch (e, s) {
|
||||||
|
_logger.severe("failed to open passkey page", e, s);
|
||||||
|
await showGenericErrorDialog(context: context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateEmailMFA(bool enableEmailMFA) async {
|
Future<void> updateEmailMFA(bool enableEmailMFA) async {
|
||||||
try {
|
try {
|
||||||
final UserDetails details =
|
final UserDetails details =
|
||||||
|
@ -56,7 +56,8 @@ class _LockScreenState extends State<LockScreen> with WidgetsBindingObserver {
|
|||||||
text: context.l10n.unlock,
|
text: context.l10n.unlock,
|
||||||
iconData: Icons.lock_open_outlined,
|
iconData: Icons.lock_open_outlined,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _showLockScreen(source: "tapUnlock");
|
// ignore: unawaited_futures
|
||||||
|
_showLockScreen(source: "tapUnlock");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:ente_auth/l10n/l10n.dart';
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
|
import 'package:ente_auth/models/account/two_factor.dart';
|
||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/ui/lifecycle_event_handler.dart';
|
import 'package:ente_auth/ui/lifecycle_event_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -129,7 +130,11 @@ class _TwoFactorAuthenticationPageState
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
UserService.instance.recoverTwoFactor(context, widget.sessionID);
|
UserService.instance.recoverTwoFactor(
|
||||||
|
context,
|
||||||
|
widget.sessionID,
|
||||||
|
TwoFactorType.totp,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:ente_auth/l10n/l10n.dart';
|
import 'package:ente_auth/l10n/l10n.dart';
|
||||||
|
import 'package:ente_auth/models/account/two_factor.dart';
|
||||||
import 'package:ente_auth/services/user_service.dart';
|
import 'package:ente_auth/services/user_service.dart';
|
||||||
import 'package:ente_auth/utils/dialog_util.dart';
|
import 'package:ente_auth/utils/dialog_util.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -9,8 +10,10 @@ class TwoFactorRecoveryPage extends StatefulWidget {
|
|||||||
final String sessionID;
|
final String sessionID;
|
||||||
final String encryptedSecret;
|
final String encryptedSecret;
|
||||||
final String secretDecryptionNonce;
|
final String secretDecryptionNonce;
|
||||||
|
final TwoFactorType type;
|
||||||
|
|
||||||
const TwoFactorRecoveryPage(
|
const TwoFactorRecoveryPage(
|
||||||
|
this.type,
|
||||||
this.sessionID,
|
this.sessionID,
|
||||||
this.encryptedSecret,
|
this.encryptedSecret,
|
||||||
this.secretDecryptionNonce, {
|
this.secretDecryptionNonce, {
|
||||||
@ -72,6 +75,7 @@ class _TwoFactorRecoveryPageState extends State<TwoFactorRecoveryPage> {
|
|||||||
? () async {
|
? () async {
|
||||||
await UserService.instance.removeTwoFactor(
|
await UserService.instance.removeTwoFactor(
|
||||||
context,
|
context,
|
||||||
|
widget.type,
|
||||||
widget.sessionID,
|
widget.sessionID,
|
||||||
_recoveryKey.text,
|
_recoveryKey.text,
|
||||||
widget.encryptedSecret,
|
widget.encryptedSecret,
|
||||||
|
@ -197,10 +197,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.17.2"
|
||||||
computer:
|
computer:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -827,10 +827,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.9.1"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -879,14 +879,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
open_filex:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: open_filex
|
|
||||||
sha256: "854aefd72dfd74219dc8c8d1767c34ec1eae64b8399a5be317bddb1ec2108915"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "4.3.2"
|
|
||||||
otp:
|
otp:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1304,10 +1296,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1"
|
version: "1.11.0"
|
||||||
step_progress_indicator:
|
step_progress_indicator:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1320,10 +1312,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.1"
|
||||||
stream_transform:
|
stream_transform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1368,26 +1360,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f
|
sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.24.9"
|
version: "1.24.3"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.1"
|
version: "0.6.0"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a
|
sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.9"
|
version: "0.5.3"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1576,10 +1568,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.1.4-beta"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1637,5 +1629,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.2.0-194.0.dev <4.0.0"
|
dart: ">=3.1.0-185.0.dev <4.0.0"
|
||||||
flutter: ">=3.10.0"
|
flutter: ">=3.10.0"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
name: ente_auth
|
name: ente_auth
|
||||||
description: ente two-factor authenticator
|
description: ente two-factor authenticator
|
||||||
version: 2.0.35+235
|
version: 2.0.42+242
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -54,13 +54,11 @@ dependencies:
|
|||||||
intl: ^0.18.0
|
intl: ^0.18.0
|
||||||
json_annotation: ^4.5.0
|
json_annotation: ^4.5.0
|
||||||
local_auth: ^2.1.7
|
local_auth: ^2.1.7
|
||||||
|
|
||||||
local_auth_android: ^1.0.31
|
local_auth_android: ^1.0.31
|
||||||
local_auth_ios: ^1.1.3
|
local_auth_ios: ^1.1.3
|
||||||
logging: ^1.0.1
|
logging: ^1.0.1
|
||||||
modal_bottom_sheet: ^3.0.0-pre
|
modal_bottom_sheet: ^3.0.0-pre
|
||||||
move_to_background: ^1.0.2
|
move_to_background: ^1.0.2
|
||||||
open_filex: ^4.3.2
|
|
||||||
otp: ^3.1.1
|
otp: ^3.1.1
|
||||||
package_info_plus: ^4.1.0
|
package_info_plus: ^4.1.0
|
||||||
password_strength: ^0.2.0
|
password_strength: ^0.2.0
|
||||||
|
@ -64,7 +64,14 @@ ente account update --email email@domain.com --dir ~/photos
|
|||||||
ente export
|
ente export
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### CLI Docs
|
||||||
|
You can view more cli documents at [docs](docs/generated/ente.md).
|
||||||
|
To update the docs, run the following command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go run main.go docs
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ var updateAccCmd = &cobra.Command{
|
|||||||
fmt.Printf("invalid app. Accepted values are 'photos', 'locker', 'auth'")
|
fmt.Printf("invalid app. Accepted values are 'photos', 'locker', 'auth'")
|
||||||
|
|
||||||
}
|
}
|
||||||
err := ctrl.UpdateAccount(context.Background(), model.UpdateAccountParams{
|
err := ctrl.UpdateAccount(context.Background(), model.AccountCommandParams{
|
||||||
Email: email,
|
Email: email,
|
||||||
App: api.StringToApp(app),
|
App: api.StringToApp(app),
|
||||||
ExportDir: &exportDir,
|
ExportDir: &exportDir,
|
||||||
@ -73,12 +73,49 @@ var updateAccCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Subcommand for 'account update'
|
||||||
|
var getTokenCmd = &cobra.Command{
|
||||||
|
Use: "get-token",
|
||||||
|
Short: "Get token for an account for a specific app",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
recoverWithLog()
|
||||||
|
app, _ := cmd.Flags().GetString("app")
|
||||||
|
email, _ := cmd.Flags().GetString("email")
|
||||||
|
if email == "" {
|
||||||
|
|
||||||
|
fmt.Println("email must be specified, use --help for more information")
|
||||||
|
// print help
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
validApps := map[string]bool{
|
||||||
|
"photos": true,
|
||||||
|
"locker": true,
|
||||||
|
"auth": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validApps[app] {
|
||||||
|
fmt.Printf("invalid app. Accepted values are 'photos', 'locker', 'auth'")
|
||||||
|
|
||||||
|
}
|
||||||
|
err := ctrl.GetToken(context.Background(), model.AccountCommandParams{
|
||||||
|
Email: email,
|
||||||
|
App: api.StringToApp(app),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error updating account: %v\n", err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// Add 'config' subcommands to the root command
|
// Add 'config' subcommands to the root command
|
||||||
rootCmd.AddCommand(accountCmd)
|
rootCmd.AddCommand(accountCmd)
|
||||||
// Add 'config' subcommands to the 'config' command
|
// Add 'config' subcommands to the 'config' command
|
||||||
updateAccCmd.Flags().String("dir", "", "update export directory")
|
updateAccCmd.Flags().String("dir", "", "update export directory")
|
||||||
updateAccCmd.Flags().String("email", "", "email address of the account to update")
|
updateAccCmd.Flags().String("email", "", "email address of the account")
|
||||||
updateAccCmd.Flags().String("app", "photos", "Specify the app, default is 'photos'")
|
updateAccCmd.Flags().String("app", "photos", "Specify the app, default is 'photos'")
|
||||||
accountCmd.AddCommand(listAccCmd, addAccCmd, updateAccCmd)
|
getTokenCmd.Flags().String("email", "", "email address of the account")
|
||||||
|
getTokenCmd.Flags().String("app", "photos", "Specify the app, default is 'photos'")
|
||||||
|
accountCmd.AddCommand(listAccCmd, addAccCmd, updateAccCmd, getTokenCmd)
|
||||||
}
|
}
|
||||||
|
90
cli/cmd/admin.go
Normal file
90
cli/cmd/admin.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ente-io/cli/pkg/model"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _adminCmd = &cobra.Command{
|
||||||
|
Use: "admin",
|
||||||
|
Short: "Commands for admin actions",
|
||||||
|
Long: "Commands for admin actions like disable or enabling 2fa, bumping up the storage limit, etc.",
|
||||||
|
}
|
||||||
|
|
||||||
|
var _userDetailsCmd = &cobra.Command{
|
||||||
|
Use: "get-user-id",
|
||||||
|
Short: "Get user id",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
recoverWithLog()
|
||||||
|
var flags = &model.AdminActionForUser{}
|
||||||
|
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
||||||
|
if f.Name == "admin-user" {
|
||||||
|
flags.AdminEmail = f.Value.String()
|
||||||
|
}
|
||||||
|
if f.Name == "user" {
|
||||||
|
flags.UserEmail = f.Value.String()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ctrl.GetUserId(context.Background(), *flags)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var _disable2faCmd = &cobra.Command{
|
||||||
|
Use: "disable-2fa",
|
||||||
|
Short: "Disable 2fa for a user",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
recoverWithLog()
|
||||||
|
var flags = &model.AdminActionForUser{}
|
||||||
|
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
||||||
|
if f.Name == "admin-user" {
|
||||||
|
flags.AdminEmail = f.Value.String()
|
||||||
|
}
|
||||||
|
if f.Name == "user" {
|
||||||
|
flags.UserEmail = f.Value.String()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
fmt.Println("Not supported yet")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var _updateFreeUserStorage = &cobra.Command{
|
||||||
|
Use: "update-subscription",
|
||||||
|
Short: "Update subscription for the free user",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
recoverWithLog()
|
||||||
|
var flags = &model.AdminActionForUser{}
|
||||||
|
noLimit := false
|
||||||
|
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
||||||
|
if f.Name == "admin-user" {
|
||||||
|
flags.AdminEmail = f.Value.String()
|
||||||
|
}
|
||||||
|
if f.Name == "user" {
|
||||||
|
flags.UserEmail = f.Value.String()
|
||||||
|
}
|
||||||
|
if f.Name == "no-limit" {
|
||||||
|
noLimit = strings.ToLower(f.Value.String()) == "true"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ctrl.UpdateFreeStorage(context.Background(), *flags, noLimit)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(_adminCmd)
|
||||||
|
_ = _userDetailsCmd.MarkFlagRequired("admin-user")
|
||||||
|
_ = _userDetailsCmd.MarkFlagRequired("user")
|
||||||
|
_userDetailsCmd.Flags().StringP("admin-user", "a", "", "The email of the admin user. (required)")
|
||||||
|
_userDetailsCmd.Flags().StringP("user", "u", "", "The email of the user to fetch details for. (required)")
|
||||||
|
_disable2faCmd.Flags().StringP("admin-user", "a", "", "The email of the admin user. (required)")
|
||||||
|
_disable2faCmd.Flags().StringP("user", "u", "", "The email of the user to disable 2FA for. (required)")
|
||||||
|
_updateFreeUserStorage.Flags().StringP("admin-user", "a", "", "The email of the admin user. (required)")
|
||||||
|
_updateFreeUserStorage.Flags().StringP("user", "u", "", "The email of the user to update subscription for. (required)")
|
||||||
|
// add a flag with no value --no-limit
|
||||||
|
_updateFreeUserStorage.Flags().String("no-limit", "True", "When true, sets 100TB as storage limit, and expiry to current date + 100 years")
|
||||||
|
_adminCmd.AddCommand(_userDetailsCmd, _disable2faCmd, _updateFreeUserStorage)
|
||||||
|
}
|
@ -3,6 +3,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ente-io/cli/pkg"
|
"github.com/ente-io/cli/pkg"
|
||||||
|
"github.com/spf13/cobra/doc"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
@ -11,7 +12,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const AppVersion = "0.1.11"
|
var version string
|
||||||
|
|
||||||
var ctrl *pkg.ClICtrl
|
var ctrl *pkg.ClICtrl
|
||||||
|
|
||||||
@ -27,10 +28,15 @@ var rootCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GenerateDocs() error {
|
||||||
|
return doc.GenMarkdownTree(rootCmd, "./docs/generated")
|
||||||
|
}
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
func Execute(controller *pkg.ClICtrl) {
|
func Execute(controller *pkg.ClICtrl, ver string) {
|
||||||
ctrl = controller
|
ctrl = controller
|
||||||
|
version = ver
|
||||||
err := rootCmd.Execute()
|
err := rootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -12,7 +12,7 @@ var versionCmd = &cobra.Command{
|
|||||||
Short: "Prints the current version",
|
Short: "Prints the current version",
|
||||||
Long: ``,
|
Long: ``,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Printf("Version %s\n", AppVersion)
|
fmt.Printf("Version %s\n", version)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
cli/config.yaml.example
Normal file
10
cli/config.yaml.example
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# You can put this configuration file in the following locations:
|
||||||
|
# - $HOME/.ente/config.yaml
|
||||||
|
# - config.yaml in the current working directory
|
||||||
|
# - $ENTE_CLI_CONFIG_PATH/config.yaml
|
||||||
|
|
||||||
|
endpoint:
|
||||||
|
api: "http://localhost:8080"
|
||||||
|
|
||||||
|
log:
|
||||||
|
http: false # log status code & time taken by requests
|
28
cli/docs/generated/ente.md
Normal file
28
cli/docs/generated/ente.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
## ente
|
||||||
|
|
||||||
|
CLI tool for exporting your photos from ente.io
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
Start by creating a config file in your home directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
ente [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for ente
|
||||||
|
-t, --toggle Help message for toggle
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente account](ente_account.md) - Manage account settings
|
||||||
|
* [ente admin](ente_admin.md) - Commands for admin actions
|
||||||
|
* [ente auth](ente_auth.md) - Authenticator commands
|
||||||
|
* [ente export](ente_export.md) - Starts the export process
|
||||||
|
* [ente version](ente_version.md) - Prints the current version
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
19
cli/docs/generated/ente_account.md
Normal file
19
cli/docs/generated/ente_account.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## ente account
|
||||||
|
|
||||||
|
Manage account settings
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for account
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente](ente.md) - CLI tool for exporting your photos from ente.io
|
||||||
|
* [ente account add](ente_account_add.md) - Add a new account
|
||||||
|
* [ente account get-token](ente_account_get-token.md) - Get token for an account for a specific app
|
||||||
|
* [ente account list](ente_account_list.md) - list configured accounts
|
||||||
|
* [ente account update](ente_account_update.md) - Update an existing account's export directory
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
19
cli/docs/generated/ente_account_add.md
Normal file
19
cli/docs/generated/ente_account_add.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## ente account add
|
||||||
|
|
||||||
|
Add a new account
|
||||||
|
|
||||||
|
```
|
||||||
|
ente account add [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for add
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente account](ente_account.md) - Manage account settings
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
21
cli/docs/generated/ente_account_get-token.md
Normal file
21
cli/docs/generated/ente_account_get-token.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
## ente account get-token
|
||||||
|
|
||||||
|
Get token for an account for a specific app
|
||||||
|
|
||||||
|
```
|
||||||
|
ente account get-token [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
--app string Specify the app, default is 'photos' (default "photos")
|
||||||
|
--email string email address of the account
|
||||||
|
-h, --help help for get-token
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente account](ente_account.md) - Manage account settings
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
19
cli/docs/generated/ente_account_list.md
Normal file
19
cli/docs/generated/ente_account_list.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## ente account list
|
||||||
|
|
||||||
|
list configured accounts
|
||||||
|
|
||||||
|
```
|
||||||
|
ente account list [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for list
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente account](ente_account.md) - Manage account settings
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
22
cli/docs/generated/ente_account_update.md
Normal file
22
cli/docs/generated/ente_account_update.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
## ente account update
|
||||||
|
|
||||||
|
Update an existing account's export directory
|
||||||
|
|
||||||
|
```
|
||||||
|
ente account update [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
--app string Specify the app, default is 'photos' (default "photos")
|
||||||
|
--dir string update export directory
|
||||||
|
--email string email address of the account
|
||||||
|
-h, --help help for update
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente account](ente_account.md) - Manage account settings
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
22
cli/docs/generated/ente_admin.md
Normal file
22
cli/docs/generated/ente_admin.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
## ente admin
|
||||||
|
|
||||||
|
Commands for admin actions
|
||||||
|
|
||||||
|
### Synopsis
|
||||||
|
|
||||||
|
Commands for admin actions like disable or enabling 2fa, bumping up the storage limit, etc.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for admin
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente](ente.md) - CLI tool for exporting your photos from ente.io
|
||||||
|
* [ente admin disable-2fa](ente_admin_disable-2fa.md) - Disable 2fa for a user
|
||||||
|
* [ente admin get-user-id](ente_admin_get-user-id.md) - Get user id
|
||||||
|
* [ente admin update-subscription](ente_admin_update-subscription.md) - Update subscription for the free user
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
21
cli/docs/generated/ente_admin_disable-2fa.md
Normal file
21
cli/docs/generated/ente_admin_disable-2fa.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
## ente admin disable-2fa
|
||||||
|
|
||||||
|
Disable 2fa for a user
|
||||||
|
|
||||||
|
```
|
||||||
|
ente admin disable-2fa [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-a, --admin-user string The email of the admin user. (required)
|
||||||
|
-h, --help help for disable-2fa
|
||||||
|
-u, --user string The email of the user to disable 2FA for. (required)
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente admin](ente_admin.md) - Commands for admin actions
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
21
cli/docs/generated/ente_admin_get-user-id.md
Normal file
21
cli/docs/generated/ente_admin_get-user-id.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
## ente admin get-user-id
|
||||||
|
|
||||||
|
Get user id
|
||||||
|
|
||||||
|
```
|
||||||
|
ente admin get-user-id [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-a, --admin-user string The email of the admin user. (required)
|
||||||
|
-h, --help help for get-user-id
|
||||||
|
-u, --user string The email of the user to fetch details for. (required)
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente admin](ente_admin.md) - Commands for admin actions
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
22
cli/docs/generated/ente_admin_update-subscription.md
Normal file
22
cli/docs/generated/ente_admin_update-subscription.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
## ente admin update-subscription
|
||||||
|
|
||||||
|
Update subscription for the free user
|
||||||
|
|
||||||
|
```
|
||||||
|
ente admin update-subscription [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-a, --admin-user string The email of the admin user. (required)
|
||||||
|
-h, --help help for update-subscription
|
||||||
|
--no-limit string When true, sets 100TB as storage limit, and expiry to current date + 100 years (default "True")
|
||||||
|
-u, --user string The email of the user to update subscription for. (required)
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente admin](ente_admin.md) - Commands for admin actions
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
16
cli/docs/generated/ente_auth.md
Normal file
16
cli/docs/generated/ente_auth.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
## ente auth
|
||||||
|
|
||||||
|
Authenticator commands
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for auth
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente](ente.md) - CLI tool for exporting your photos from ente.io
|
||||||
|
* [ente auth decrypt](ente_auth_decrypt.md) - Decrypt authenticator export
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
19
cli/docs/generated/ente_auth_decrypt.md
Normal file
19
cli/docs/generated/ente_auth_decrypt.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## ente auth decrypt
|
||||||
|
|
||||||
|
Decrypt authenticator export
|
||||||
|
|
||||||
|
```
|
||||||
|
ente auth decrypt [input] [output] [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for decrypt
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente auth](ente_auth.md) - Authenticator commands
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
19
cli/docs/generated/ente_export.md
Normal file
19
cli/docs/generated/ente_export.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## ente export
|
||||||
|
|
||||||
|
Starts the export process
|
||||||
|
|
||||||
|
```
|
||||||
|
ente export [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for export
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente](ente.md) - CLI tool for exporting your photos from ente.io
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
19
cli/docs/generated/ente_version.md
Normal file
19
cli/docs/generated/ente_version.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## ente version
|
||||||
|
|
||||||
|
Prints the current version
|
||||||
|
|
||||||
|
```
|
||||||
|
ente version [flags]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
```
|
||||||
|
-h, --help help for version
|
||||||
|
```
|
||||||
|
|
||||||
|
### SEE ALSO
|
||||||
|
|
||||||
|
* [ente](ente.md) - CLI tool for exporting your photos from ente.io
|
||||||
|
|
||||||
|
###### Auto generated by spf13/cobra on 13-Mar-2024
|
27
cli/docs/selfhost.md
Normal file
27
cli/docs/selfhost.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
## Self Hosting
|
||||||
|
If you are self-hosting the server, you can still configure CLI to export data & perform basic admin actions.
|
||||||
|
|
||||||
|
To do this, first configure the CLI to point to your server.
|
||||||
|
Define a config.yaml and put it either in the same directory as CLI binary or path defined in env variable `ENTE_CLI_CONFIG_PATH`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
endpoint:
|
||||||
|
api: "http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
You should be able to [add an account](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_add.md), and subsequently increase the [storage and account validity](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_admin_update-subscription.md) using the CLI.
|
||||||
|
|
||||||
|
|
||||||
|
For the admin actions, you first need to whitelist admin users. You can create `server/museum.yaml`, and whitelist add the admin userID `internal.admins`. See [local.yaml](https://github.com/ente-io/ente/blob/main/server/configurations/local.yaml#L211C1-L232C1) in the server source code for details about how to define this.
|
||||||
|
|
||||||
|
You can use [account list](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_list.md) command to find the user id of any account.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# ....
|
||||||
|
|
||||||
|
internal:
|
||||||
|
admins:
|
||||||
|
# - 1580559962386440
|
||||||
|
|
||||||
|
# ....
|
||||||
|
```
|
@ -12,10 +12,12 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alessio/shellescape v1.4.1 // indirect
|
github.com/alessio/shellescape v1.4.1 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/danieljoos/wincred v1.2.0 // indirect
|
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -48,6 +48,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
|||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
|
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
|
||||||
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
|
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
|
||||||
@ -168,6 +169,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||||
|
57
cli/internal/api/admin.go
Normal file
57
cli/internal/api/admin.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ente-io/cli/internal/api/models"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) GetUserIdFromEmail(ctx context.Context, email string) (*models.UserDetails, error) {
|
||||||
|
var res models.UserDetails
|
||||||
|
r, err := c.restClient.R().
|
||||||
|
SetContext(ctx).
|
||||||
|
SetResult(&res).
|
||||||
|
SetQueryParam("email", email).
|
||||||
|
Get("/admin/user/")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.IsError() {
|
||||||
|
return nil, &ApiError{
|
||||||
|
StatusCode: r.StatusCode(),
|
||||||
|
Message: r.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
func (c *Client) UpdateFreePlanSub(ctx context.Context, userDetails *models.UserDetails, storageInBytes int64, expiryTimeInMicro int64) error {
|
||||||
|
var res interface{}
|
||||||
|
if userDetails.Subscription.ProductID != "free" {
|
||||||
|
return fmt.Errorf("user is not on free plan")
|
||||||
|
}
|
||||||
|
payload := map[string]interface{}{
|
||||||
|
"userID": userDetails.User.ID,
|
||||||
|
"expiryTime": expiryTimeInMicro,
|
||||||
|
"transactionID": fmt.Sprintf("cli-on-%d", time.Now().Unix()),
|
||||||
|
"productID": "free",
|
||||||
|
"paymentProvider": "",
|
||||||
|
"storage": storageInBytes,
|
||||||
|
}
|
||||||
|
r, err := c.restClient.R().
|
||||||
|
SetContext(ctx).
|
||||||
|
SetResult(&res).
|
||||||
|
SetBody(payload).
|
||||||
|
Put("/admin/user/subscription")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.IsError() {
|
||||||
|
return &ApiError{
|
||||||
|
StatusCode: r.StatusCode(),
|
||||||
|
Message: r.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
@ -2,19 +2,30 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/ente-io/cli/utils/constants"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
downloadHost = "https://files.ente.io/?fileID="
|
downloadHost = "https://files.ente.io/?fileID="
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func downloadUrl(fileID int64) string {
|
||||||
|
apiEndpoint := viper.GetString("endpoint.api")
|
||||||
|
if apiEndpoint == "" || strings.Compare(apiEndpoint, constants.EnteApiUrl) == 0 {
|
||||||
|
return downloadHost + strconv.FormatInt(fileID, 10)
|
||||||
|
}
|
||||||
|
return apiEndpoint + "/files/download/" + strconv.FormatInt(fileID, 10)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) DownloadFile(ctx context.Context, fileID int64, absolutePath string) error {
|
func (c *Client) DownloadFile(ctx context.Context, fileID int64, absolutePath string) error {
|
||||||
req := c.downloadClient.R().
|
req := c.downloadClient.R().
|
||||||
SetContext(ctx).
|
SetContext(ctx).
|
||||||
SetOutput(absolutePath)
|
SetOutput(absolutePath)
|
||||||
attachToken(req)
|
attachToken(req)
|
||||||
r, err := req.Get(downloadHost + strconv.FormatInt(fileID, 10))
|
r, err := req.Get(downloadUrl(fileID))
|
||||||
if r.IsError() {
|
if r.IsError() {
|
||||||
return &ApiError{
|
return &ApiError{
|
||||||
StatusCode: r.StatusCode(),
|
StatusCode: r.StatusCode(),
|
||||||
|
@ -30,6 +30,16 @@ func logRequest(req *resty.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// log query params if present
|
||||||
|
if len(req.QueryParam) > 0 {
|
||||||
|
fmt.Println(color.GreenString("Query Params:"))
|
||||||
|
for k, v := range req.QueryParam {
|
||||||
|
if k == TokenQuery {
|
||||||
|
v = []string{"REDACTED"}
|
||||||
|
}
|
||||||
|
fmt.Printf("%s: %s\n", color.CyanString(k), color.YellowString(strings.Join(v, ",")))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func logResponse(resp *resty.Response) {
|
func logResponse(resp *resty.Response) {
|
||||||
|
16
cli/internal/api/models/user_details.go
Normal file
16
cli/internal/api/models/user_details.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type UserDetails struct {
|
||||||
|
User struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
} `json:"user"`
|
||||||
|
Usage int64 `json:"usage"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
|
||||||
|
Subscription struct {
|
||||||
|
ExpiryTime int64 `json:"expiryTime"`
|
||||||
|
Storage int64 `json:"storage"`
|
||||||
|
ProductID string `json:"productID"`
|
||||||
|
PaymentProvider string `json:"paymentProvider"`
|
||||||
|
} `json:"subscription"`
|
||||||
|
}
|
@ -5,11 +5,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ente-io/cli/internal/api"
|
"github.com/ente-io/cli/internal/api"
|
||||||
|
"golang.org/x/term"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetSensitiveField(label string) (string, error) {
|
func GetSensitiveField(label string) (string, error) {
|
||||||
@ -81,6 +82,79 @@ func GetCode(promptText string, length int) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseStorageSize parses a string representing a storage size (e.g., "500MB", "2GB") into bytes.
|
||||||
|
func parseStorageSize(input string) (int64, error) {
|
||||||
|
units := map[string]int64{
|
||||||
|
"MB": 1 << 20,
|
||||||
|
"GB": 1 << 30,
|
||||||
|
"TB": 1 << 40,
|
||||||
|
}
|
||||||
|
re := regexp.MustCompile(`(?i)^(\d+(?:\.\d+)?)(MB|GB|TB)$`)
|
||||||
|
matches := re.FindStringSubmatch(input)
|
||||||
|
|
||||||
|
if matches == nil {
|
||||||
|
return 0, errors.New("invalid format")
|
||||||
|
}
|
||||||
|
|
||||||
|
number, err := strconv.ParseFloat(matches[1], 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("invalid number: %s", matches[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
unit := strings.ToUpper(matches[2])
|
||||||
|
bytes := int64(number * float64(units[unit]))
|
||||||
|
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConfirmAction(promptText string) (bool, error) {
|
||||||
|
for {
|
||||||
|
input, err := GetUserInput(promptText)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if input == "" {
|
||||||
|
log.Fatal("No input entered")
|
||||||
|
return false, errors.New("invalid input. Please enter 'y' or 'n'")
|
||||||
|
}
|
||||||
|
if input == "c" {
|
||||||
|
return false, errors.New("cancelled")
|
||||||
|
}
|
||||||
|
if input == "y" {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if input == "n" {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
fmt.Println("Invalid input. Please enter 'y' or 'n'.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStorageSize prompts the user for a storage size and returns the size in bytes.
|
||||||
|
func GetStorageSize(promptText string) (int64, error) {
|
||||||
|
for {
|
||||||
|
input, err := GetUserInput(promptText)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if input == "" {
|
||||||
|
log.Fatal("No storage size entered")
|
||||||
|
return 0, errors.New("no storage size entered")
|
||||||
|
}
|
||||||
|
if input == "c" {
|
||||||
|
return 0, errors.New("storage size entry cancelled")
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := parseStorageSize(input)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Invalid storage size format. Please use a valid format like '500MB', '2GB'.")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetExportDir() string {
|
func GetExportDir() string {
|
||||||
for {
|
for {
|
||||||
exportDir, err := GetUserInput("Enter export directory")
|
exportDir, err := GetUserInput("Enter export directory")
|
||||||
|
36
cli/main.go
36
cli/main.go
@ -8,12 +8,15 @@ import (
|
|||||||
"github.com/ente-io/cli/pkg"
|
"github.com/ente-io/cli/pkg"
|
||||||
"github.com/ente-io/cli/pkg/secrets"
|
"github.com/ente-io/cli/pkg/secrets"
|
||||||
"github.com/ente-io/cli/utils/constants"
|
"github.com/ente-io/cli/utils/constants"
|
||||||
|
"github.com/spf13/viper"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var AppVersion = "0.1.12"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cliDBPath, err := GetCLIConfigPath()
|
cliDBPath, err := GetCLIConfigPath()
|
||||||
if secrets.IsRunningInContainer() {
|
if secrets.IsRunningInContainer() {
|
||||||
@ -23,10 +26,10 @@ func main() {
|
|||||||
log.Fatalf("Please mount a volume to %s to persist cli data\n%v\n", cliDBPath, err)
|
log.Fatalf("Please mount a volume to %s to persist cli data\n%v\n", cliDBPath, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not create cli config path\n%v\n", err)
|
log.Fatalf("Could not create cli config path\n%v\n", err)
|
||||||
}
|
}
|
||||||
|
initConfig(cliDBPath)
|
||||||
newCliPath := fmt.Sprintf("%s/ente-cli.db", cliDBPath)
|
newCliPath := fmt.Sprintf("%s/ente-cli.db", cliDBPath)
|
||||||
if !strings.HasPrefix(cliDBPath, "/") {
|
if !strings.HasPrefix(cliDBPath, "/") {
|
||||||
oldCliPath := fmt.Sprintf("%sente-cli.db", cliDBPath)
|
oldCliPath := fmt.Sprintf("%sente-cli.db", cliDBPath)
|
||||||
@ -48,8 +51,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
ctrl := pkg.ClICtrl{
|
ctrl := pkg.ClICtrl{
|
||||||
Client: api.NewClient(api.Params{
|
Client: api.NewClient(api.Params{
|
||||||
Debug: false,
|
Debug: viper.GetBool("log.http"),
|
||||||
//Host: "http://localhost:8080",
|
Host: viper.GetString("endpoint.api"),
|
||||||
}),
|
}),
|
||||||
DB: db,
|
DB: db,
|
||||||
KeyHolder: secrets.NewKeyHolder(secrets.GetOrCreateClISecret()),
|
KeyHolder: secrets.NewKeyHolder(secrets.GetOrCreateClISecret()),
|
||||||
@ -63,7 +66,32 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
cmd.Execute(&ctrl)
|
|
||||||
|
if len(os.Args) == 2 && os.Args[1] == "docs" {
|
||||||
|
log.Println("Generating docs")
|
||||||
|
err = cmd.GenerateDocs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd.Execute(&ctrl, AppVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initConfig(cliConfigPath string) {
|
||||||
|
viper.SetConfigName("config") // name of config file (without extension)
|
||||||
|
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
|
||||||
|
viper.AddConfigPath(cliConfigPath + "/") // path to look for the config file in
|
||||||
|
viper.AddConfigPath(".") // optionally look for config in the working directory
|
||||||
|
|
||||||
|
viper.SetDefault("endpoint.api", constants.EnteApiUrl)
|
||||||
|
viper.SetDefault("log.http", false)
|
||||||
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
|
} else {
|
||||||
|
// Config file was found but another error was produced
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCLIConfigPath returns the path to the .ente-cli folder and creates it if it doesn't exist.
|
// GetCLIConfigPath returns the path to the .ente-cli folder and creates it if it doesn't exist.
|
||||||
|
@ -142,7 +142,7 @@ func (c *ClICtrl) ListAccounts(cxt context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClICtrl) UpdateAccount(ctx context.Context, params model.UpdateAccountParams) error {
|
func (c *ClICtrl) UpdateAccount(ctx context.Context, params model.AccountCommandParams) error {
|
||||||
accounts, err := c.GetAccounts(ctx)
|
accounts, err := c.GetAccounts(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -177,5 +177,27 @@ func (c *ClICtrl) UpdateAccount(ctx context.Context, params model.UpdateAccountP
|
|||||||
return b.Put([]byte(accountKey), accInfoBytes)
|
return b.Put([]byte(accountKey), accInfoBytes)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClICtrl) GetToken(ctx context.Context, params model.AccountCommandParams) error {
|
||||||
|
accounts, err := c.GetAccounts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var acc *model.Account
|
||||||
|
for _, a := range accounts {
|
||||||
|
if a.Email == params.Email && a.App == params.App {
|
||||||
|
acc = &a
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if acc == nil {
|
||||||
|
return fmt.Errorf("account not found, use `account list` to list accounts")
|
||||||
|
}
|
||||||
|
secretInfo, err := c.KeyHolder.LoadSecrets(*acc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(secretInfo.TokenStr())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
114
cli/pkg/admin_actions.go
Normal file
114
cli/pkg/admin_actions.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package pkg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ente-io/cli/internal"
|
||||||
|
"github.com/ente-io/cli/pkg/model"
|
||||||
|
"github.com/ente-io/cli/utils"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *ClICtrl) GetUserId(ctx context.Context, params model.AdminActionForUser) error {
|
||||||
|
accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
id, err := c.Client.GetUserIdFromEmail(accountCtx, params.UserEmail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(id.User.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClICtrl) UpdateFreeStorage(ctx context.Context, params model.AdminActionForUser, noLimit bool) error {
|
||||||
|
accountCtx, err := c.buildAdminContext(ctx, params.AdminEmail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
userDetails, err := c.Client.GetUserIdFromEmail(accountCtx, params.UserEmail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if noLimit {
|
||||||
|
// set storage to 100TB and expiry to + 100 years
|
||||||
|
err := c.Client.UpdateFreePlanSub(accountCtx, userDetails, 100*1024*1024*1024*1024, time.Now().AddDate(100, 0, 0).UnixMicro())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
fmt.Println("Successfully updated storage and expiry date for user")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
storageSize, err := internal.GetStorageSize("Enter a storage size (e.g.'5MB', '10GB', '2Tb'): ")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: %v", err)
|
||||||
|
}
|
||||||
|
dateStr, err := internal.GetUserInput("Enter sub expiry date in YYYY-MM-DD format (e.g.'2040-12-31')")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error: %v", err)
|
||||||
|
}
|
||||||
|
date, err := _parseDateOrDateTime(dateStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Updating storage for user %s to %s (old %s) with new expirty %s (old %s) \n",
|
||||||
|
params.UserEmail,
|
||||||
|
utils.ByteCountDecimalGIB(storageSize), utils.ByteCountDecimalGIB(userDetails.Subscription.Storage),
|
||||||
|
date.Format("2006-01-02"),
|
||||||
|
time.UnixMicro(userDetails.Subscription.ExpiryTime).Format("2006-01-02"))
|
||||||
|
// press y to confirm
|
||||||
|
confirmed, _ := internal.ConfirmAction("Are you sure you want to update the storage ('y' or 'n')?")
|
||||||
|
if !confirmed {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
err := c.Client.UpdateFreePlanSub(accountCtx, userDetails, storageSize, date.UnixMicro())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
fmt.Println("Successfully updated storage and expiry date for user")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClICtrl) buildAdminContext(ctx context.Context, adminEmail string) (context.Context, error) {
|
||||||
|
accounts, err := c.GetAccounts(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var acc *model.Account
|
||||||
|
for _, a := range accounts {
|
||||||
|
if a.Email == adminEmail {
|
||||||
|
acc = &a
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if acc == nil {
|
||||||
|
return nil, fmt.Errorf("account not found for %s, use `account list` to list accounts", adminEmail)
|
||||||
|
}
|
||||||
|
secretInfo, err := c.KeyHolder.LoadSecrets(*acc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
accountCtx := c.buildRequestContext(ctx, *acc)
|
||||||
|
c.Client.AddToken(acc.AccountKey(), secretInfo.TokenStr())
|
||||||
|
return accountCtx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _parseDateOrDateTime(input string) (time.Time, error) {
|
||||||
|
var layout string
|
||||||
|
if strings.Contains(input, " ") {
|
||||||
|
// If the input contains a space, assume it's a date-time format
|
||||||
|
layout = "2006-01-02 15:04:05"
|
||||||
|
} else {
|
||||||
|
// If there's no space, assume it's just a date
|
||||||
|
layout = "2006-01-02"
|
||||||
|
}
|
||||||
|
return time.Parse(layout, input)
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ente-io/cli/internal/api"
|
"github.com/ente-io/cli/internal/api"
|
||||||
)
|
)
|
||||||
@ -17,7 +18,7 @@ type Account struct {
|
|||||||
ExportDir string `json:"exportDir"`
|
ExportDir string `json:"exportDir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateAccountParams struct {
|
type AccountCommandParams struct {
|
||||||
Email string
|
Email string
|
||||||
App api.App
|
App api.App
|
||||||
ExportDir *string
|
ExportDir *string
|
||||||
@ -37,3 +38,7 @@ type AccSecretInfo struct {
|
|||||||
Token []byte
|
Token []byte
|
||||||
PublicKey []byte
|
PublicKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *AccSecretInfo) TokenStr() string {
|
||||||
|
return base64.URLEncoding.EncodeToString(a.Token)
|
||||||
|
}
|
||||||
|
6
cli/pkg/model/admin.go
Normal file
6
cli/pkg/model/admin.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type AdminActionForUser struct {
|
||||||
|
UserEmail string
|
||||||
|
AdminEmail string
|
||||||
|
}
|
@ -1,5 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Fetch the latest tag that starts with "cli-"
|
||||||
|
# shellcheck disable=SC2046
|
||||||
|
# shellcheck disable=SC2006
|
||||||
|
LATEST_TAG=$(git describe --tags `git rev-list --tags='cli-*' --max-count=1`)
|
||||||
|
|
||||||
|
# Check if the LATEST_TAG variable is empty
|
||||||
|
if [ -z "$LATEST_TAG" ]; then
|
||||||
|
echo "No 'cli-' tag found. Exiting..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
VERSION=${LATEST_TAG#cli-}
|
||||||
# Create a "bin" directory if it doesn't exist
|
# Create a "bin" directory if it doesn't exist
|
||||||
mkdir -p bin
|
mkdir -p bin
|
||||||
|
|
||||||
@ -29,7 +40,7 @@ do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Build the binary and place it in the "bin" directory
|
# Build the binary and place it in the "bin" directory
|
||||||
go build -ldflags="-s -w" -trimpath -o "bin/$BINARY_NAME" main.go
|
go build -ldflags="-X main.AppVersion=${VERSION} -s -w" -trimpath -o "bin/$BINARY_NAME" main.go
|
||||||
|
|
||||||
# Print a message indicating the build is complete for the current OS and architecture
|
# Print a message indicating the build is complete for the current OS and architecture
|
||||||
echo "Built for $OS ($ARCH) as bin/$BINARY_NAME"
|
echo "Built for $OS ($ARCH) as bin/$BINARY_NAME"
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
const CliDataPath = "/cli-data/"
|
const CliDataPath = "/cli-data/"
|
||||||
|
const EnteApiUrl = "https://api.ente.io"
|
||||||
|
31
cli/utils/convert.go
Normal file
31
cli/utils/convert.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ByteCountDecimal(b int64) string {
|
||||||
|
const unit = 1000
|
||||||
|
if b < unit {
|
||||||
|
return fmt.Sprintf("%d B", b)
|
||||||
|
}
|
||||||
|
div, exp := int64(unit), 0
|
||||||
|
for n := b / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
||||||
|
}
|
||||||
|
|
||||||
|
func ByteCountDecimalGIB(b int64) string {
|
||||||
|
const unit = 1024
|
||||||
|
if b < unit {
|
||||||
|
return fmt.Sprintf("%d B", b)
|
||||||
|
}
|
||||||
|
div, exp := int64(unit), 0
|
||||||
|
for n := b / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -10,16 +9,3 @@ func TimeTrack(start time.Time, name string) {
|
|||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
log.Printf("%s took %s", name, elapsed)
|
log.Printf("%s took %s", name, elapsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ByteCountDecimal(b int64) string {
|
|
||||||
const unit = 1000
|
|
||||||
if b < unit {
|
|
||||||
return fmt.Sprintf("%d B", b)
|
|
||||||
}
|
|
||||||
div, exp := int64(unit), 0
|
|
||||||
for n := b / unit; n >= unit; n /= unit {
|
|
||||||
div *= unit
|
|
||||||
exp++
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
|
||||||
}
|
|
||||||
|
3
desktop/.github/workflows/build.yml
vendored
3
desktop/.github/workflows/build.yml
vendored
@ -52,7 +52,4 @@ jobs:
|
|||||||
# macOS notarization API key
|
# macOS notarization API key
|
||||||
API_KEY_ID: ${{ secrets.api_key_id }}
|
API_KEY_ID: ${{ secrets.api_key_id }}
|
||||||
API_KEY_ISSUER_ID: ${{ secrets.api_key_issuer_id}}
|
API_KEY_ISSUER_ID: ${{ secrets.api_key_issuer_id}}
|
||||||
# setry crash reporting token
|
|
||||||
SENTRY_AUTH_TOKEN: ${{secrets.sentry_auth_token}}
|
|
||||||
NEXT_PUBLIC_DISABLE_SENTRY: ${{secrets.next_public_disable_sentry}}
|
|
||||||
USE_HARD_LINKS: false
|
USE_HARD_LINKS: false
|
||||||
|
29
desktop/.gitignore
vendored
29
desktop/.gitignore
vendored
@ -1,12 +1,21 @@
|
|||||||
node_modules
|
# Node
|
||||||
app
|
node_modules/
|
||||||
.next/
|
|
||||||
dist
|
# macOS
|
||||||
.vscode
|
|
||||||
buildingSteps.md
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea/
|
|
||||||
build/.DS_Store
|
# Editors
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Local env files
|
||||||
.env
|
.env
|
||||||
.electron-symbols/
|
.env.*.local
|
||||||
models/
|
|
||||||
|
# tsc transpiles src/**/*.ts and emits the generated JS into app
|
||||||
|
app/
|
||||||
|
|
||||||
|
# out is a symlink to the photos web app's dir
|
||||||
|
out
|
||||||
|
|
||||||
|
# electron-builder
|
||||||
|
dist/
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
branch="$(git rev-parse --abbrev-ref HEAD)"
|
|
||||||
|
|
||||||
if [ "$branch" = "main" ]; then
|
|
||||||
echo "You can't commit directly to main branch"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
npx lint-staged
|
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"trailingComma": "es5",
|
"plugins": [
|
||||||
"singleQuote": true,
|
"prettier-plugin-organize-imports",
|
||||||
"bracketSameLine": true
|
"prettier-plugin-packagejson"
|
||||||
}
|
]
|
||||||
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
network-timeout 500000
|
|
@ -4,128 +4,128 @@
|
|||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
- Option to select file download location.
|
- Option to select file download location.
|
||||||
- Add support for searching popular cities
|
- Add support for searching popular cities
|
||||||
- Sorted duplicates in desecending order of size
|
- Sorted duplicates in desecending order of size
|
||||||
- Add Counter to upload section
|
- Add Counter to upload section
|
||||||
- Display full name and collection name on hover on dedupe screen photos
|
- Display full name and collection name on hover on dedupe screen photos
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fix add to album padding issue
|
- Fix add to album padding issue
|
||||||
- Fix double uncategorized album issue
|
- Fix double uncategorized album issue
|
||||||
- Hide Hidden collection files from all section
|
- Hide Hidden collection files from all section
|
||||||
|
|
||||||
## v1.6.62
|
## v1.6.62
|
||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
- Integrated onnx clip runner
|
- Integrated onnx clip runner
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixes login button requiring double click issue
|
- Fixes login button requiring double click issue
|
||||||
- Fixes Collection sort state not preserved issue
|
- Fixes Collection sort state not preserved issue
|
||||||
- Fixes continuous export causing app crash
|
- Fixes continuous export causing app crash
|
||||||
- Improves ML related copies for better distinction from clip
|
- Improves ML related copies for better distinction from clip
|
||||||
- Added Better favicon for light mode
|
- Added Better favicon for light mode
|
||||||
- Fixed face indexing issues
|
- Fixed face indexing issues
|
||||||
- Fixed thumbnail load issue
|
- Fixed thumbnail load issue
|
||||||
|
|
||||||
## v1.6.60
|
## v1.6.60
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fix Thumbnail Orientation issue
|
- Fix Thumbnail Orientation issue
|
||||||
- Fix ML logging issue
|
- Fix ML logging issue
|
||||||
|
|
||||||
## v1.6.59
|
## v1.6.59
|
||||||
|
|
||||||
### New
|
### New
|
||||||
|
|
||||||
- Added arm64 builds for linux
|
- Added arm64 builds for linux
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fix Editor file not loading issue
|
- Fix Editor file not loading issue
|
||||||
- Fix ML results missing thumbnail issue
|
- Fix ML results missing thumbnail issue
|
||||||
|
|
||||||
## v1.6.58
|
## v1.6.58
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fix File load issue
|
- Fix File load issue
|
||||||
|
|
||||||
## v1.6.57
|
## v1.6.57
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- Added encrypted Disk caching for files
|
- Added encrypted Disk caching for files
|
||||||
- Added option to customize cache folder location
|
- Added option to customize cache folder location
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixed caching issue,causing multiple download of file during ml sync
|
- Fixed caching issue,causing multiple download of file during ml sync
|
||||||
|
|
||||||
## v1.6.55
|
## v1.6.55
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Added manage family portal option if add-on is active
|
- Added manage family portal option if add-on is active
|
||||||
- Fixed filename date parsing issue
|
- Fixed filename date parsing issue
|
||||||
- Fixed storage limit ui glitch
|
- Fixed storage limit ui glitch
|
||||||
- Fixed dedupe page layout issue
|
- Fixed dedupe page layout issue
|
||||||
- Fixed ElectronAPI refactoring issue
|
- Fixed ElectronAPI refactoring issue
|
||||||
- Fixed Search related issues
|
- Fixed Search related issues
|
||||||
|
|
||||||
## v1.6.54
|
## v1.6.54
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- Added support for HEIC and raw image in photo editor
|
- Added support for HEIC and raw image in photo editor
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixed 16bit HDR HEIC images support
|
- Fixed 16bit HDR HEIC images support
|
||||||
- Fixed blocked login due safe storage issue
|
- Fixed blocked login due safe storage issue
|
||||||
- Fixed Search related issues
|
- Fixed Search related issues
|
||||||
- Fixed issue of watch folder not cleared on logout
|
- Fixed issue of watch folder not cleared on logout
|
||||||
- other under the hood ui/ux improvements
|
- other under the hood ui/ux improvements
|
||||||
|
|
||||||
## v1.6.53
|
## v1.6.53
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixed watch folder disabled issue
|
- Fixed watch folder disabled issue
|
||||||
- Fixed BF Add on related issues
|
- Fixed BF Add on related issues
|
||||||
- Fixed clip sync issue and added better logging
|
- Fixed clip sync issue and added better logging
|
||||||
- Fixed mov file upload
|
- Fixed mov file upload
|
||||||
- Fixed clip extraction related issue
|
- Fixed clip extraction related issue
|
||||||
|
|
||||||
## v1.6.52
|
## v1.6.52
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- Added Clip Desktop on windows
|
- Added Clip Desktop on windows
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- fixed google json matching issue
|
- fixed google json matching issue
|
||||||
- other under-the-hood changes to improve performance and bug fixes
|
- other under-the-hood changes to improve performance and bug fixes
|
||||||
|
|
||||||
## v1.6.50
|
## v1.6.50
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- Added Clip desktop
|
- Added Clip desktop
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixed desktop downloaded file had extra dot in the name
|
- Fixed desktop downloaded file had extra dot in the name
|
||||||
- Cleanup error messages
|
- Cleanup error messages
|
||||||
- fix the motion photo clustering issue
|
- fix the motion photo clustering issue
|
||||||
- Add option to disable cf proxy locally
|
- Add option to disable cf proxy locally
|
||||||
- other under-the-hood changes to improve UX
|
- other under-the-hood changes to improve UX
|
||||||
|
|
||||||
## v1.6.49
|
## v1.6.49
|
||||||
|
|
||||||
@ -137,54 +137,54 @@ Check out our [blog](https://ente.io/blog/introducing-web-desktop-photo-editor/)
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixed misaligned icons in photo-viewer
|
- Fixed misaligned icons in photo-viewer
|
||||||
- Fixed issue with Motion photo upload
|
- Fixed issue with Motion photo upload
|
||||||
- Fixed issue with Live-photo upload
|
- Fixed issue with Live-photo upload
|
||||||
- other minor ux improvement
|
- other minor ux improvement
|
||||||
|
|
||||||
## v1.6.46
|
## v1.6.46
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixes OOM crashes during file upload [#1379](https://github.com/ente-io/photos-web/pull/1379)
|
- Fixes OOM crashes during file upload [#1379](https://github.com/ente-io/photos-web/pull/1379)
|
||||||
|
|
||||||
## v1.6.45
|
## v1.6.45
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Fixed app keeps reloading issue [#235](https://github.com/ente-io/photos-desktop/pull/235)
|
- Fixed app keeps reloading issue [#235](https://github.com/ente-io/photos-desktop/pull/235)
|
||||||
- Fixed dng and arw preview issue [#1378](https://github.com/ente-io/photos-web/pull/1378)
|
- Fixed dng and arw preview issue [#1378](https://github.com/ente-io/photos-web/pull/1378)
|
||||||
- Added view crash report option (help menu) for user to share electron crash report locally
|
- Added view crash report option (help menu) for user to share electron crash report locally
|
||||||
|
|
||||||
## v1.6.44
|
## v1.6.44
|
||||||
|
|
||||||
- Upgraded electron to get latest security patches and other improvements.
|
- Upgraded electron to get latest security patches and other improvements.
|
||||||
|
|
||||||
## v1.6.43
|
## v1.6.43
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- #### Check for update and changelog option
|
- #### Check for update and changelog option
|
||||||
|
|
||||||
Added options to check for update manually and a view changelog via the app menubar
|
Added options to check for update manually and a view changelog via the app menubar
|
||||||
|
|
||||||
- #### Opt out of crash reporting
|
- #### Opt out of crash reporting
|
||||||
|
|
||||||
Added option to out of a crash reporting, it can accessed from the settings -> preferences -> disable crash reporting
|
Added option to out of a crash reporting, it can accessed from the settings -> preferences -> disable crash reporting
|
||||||
|
|
||||||
- #### Type search
|
- #### Type search
|
||||||
|
|
||||||
Added new search option to search files based on file type i.e, image, video, live-photo.
|
Added new search option to search files based on file type i.e, image, video, live-photo.
|
||||||
|
|
||||||
- #### Manual Convert Button
|
- #### Manual Convert Button
|
||||||
|
|
||||||
In case the video is not playable, Now there is a convert button which can be used to trigger conversion of the video to supported format.
|
In case the video is not playable, Now there is a convert button which can be used to trigger conversion of the video to supported format.
|
||||||
|
|
||||||
- #### File Download Progress
|
- #### File Download Progress
|
||||||
|
|
||||||
The file loader now also shows the exact percentage download progress, instead of just a simple loader.
|
The file loader now also shows the exact percentage download progress, instead of just a simple loader.
|
||||||
|
|
||||||
- #### Bug fixes & other enhancements
|
- #### Bug fixes & other enhancements
|
||||||
|
|
||||||
We have squashed a few pesky bugs that were reported by our community
|
We have squashed a few pesky bugs that were reported by our community
|
||||||
|
|
||||||
@ -192,21 +192,21 @@ Check out our [blog](https://ente.io/blog/introducing-web-desktop-photo-editor/)
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- #### Hidden albums
|
- #### Hidden albums
|
||||||
|
|
||||||
You can now hide albums, just like individual memories.
|
You can now hide albums, just like individual memories.
|
||||||
|
|
||||||
- #### Email verification
|
- #### Email verification
|
||||||
|
|
||||||
We have now made email verification optional, so you can sign in with just your email address and password, without waiting for a verification code.
|
We have now made email verification optional, so you can sign in with just your email address and password, without waiting for a verification code.
|
||||||
|
|
||||||
You can opt in / out of email verification from Settings > Security.
|
You can opt in / out of email verification from Settings > Security.
|
||||||
|
|
||||||
- #### Download Album
|
- #### Download Album
|
||||||
|
|
||||||
You can now chose the download location for downloading albums. Along with that we have also added progress bar for album download.
|
You can now chose the download location for downloading albums. Along with that we have also added progress bar for album download.
|
||||||
|
|
||||||
- #### Bug fixes & other enhancements
|
- #### Bug fixes & other enhancements
|
||||||
|
|
||||||
We have squashed a few pesky bugs that were reported by our community
|
We have squashed a few pesky bugs that were reported by our community
|
||||||
|
|
||||||
|
@ -10,33 +10,28 @@ To know more about Ente, see [our main README](../README.md) or visit
|
|||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
|
|
||||||
> [!CAUTION]
|
|
||||||
>
|
|
||||||
> We moved a few things around when switching to a monorepo recently, so this
|
|
||||||
> folder might not build with the instructions below. Hang tight, we're on it,
|
|
||||||
> will fix things if.
|
|
||||||
|
|
||||||
Fetch submodules
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git submodule update --init --recursive
|
|
||||||
```
|
|
||||||
|
|
||||||
Install dependencies
|
Install dependencies
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the app
|
Run in development mode (with hot reload)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn start
|
yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
To recompile automatically using electron-reload, run this in a separate
|
> [!CAUTION]
|
||||||
terminal:
|
>
|
||||||
|
> `yarn dev` is currently not working (we'll fix soon). If you just want to
|
||||||
|
> build from source and use the generated binary, use `yarn build`.
|
||||||
|
|
||||||
```bash
|
Or create a binary for your platform
|
||||||
yarn watch
|
|
||||||
|
```sh
|
||||||
|
yarn build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
That's the gist of it. For more development related documentation, see
|
||||||
|
[docs](docs/README.md).
|
||||||
|
@ -1,20 +1,30 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>ente Photos</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body style="background-color: black">
|
||||||
<meta charset="UTF-8">
|
<div
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
style="
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
height: 95vh;
|
||||||
<title>ente Photos</title>
|
width: 96vw;
|
||||||
</head>
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
<body style="background-color: black;">
|
color: white;
|
||||||
<div style=" height: 95vh;width: 96vw; display: grid; place-items: center; color: white;">
|
"
|
||||||
<div>
|
>
|
||||||
<div style="margin-bottom: 10px;">Site unreachable, please try again later</div>
|
<div>
|
||||||
<button onClick="window[`ElectronAPIs`].reloadWindow()">Reload</button>
|
<div style="margin-bottom: 10px">
|
||||||
|
Site unreachable, please try again later
|
||||||
|
</div>
|
||||||
|
<button onClick="window[`ElectronAPIs`].reloadWindow()">
|
||||||
|
Reload
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</body>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,30 +1,50 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>ente Photos</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
<head>
|
<body style="background-color: black">
|
||||||
<meta charset="UTF-8">
|
<div
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
style="
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
display: flex;
|
||||||
<title>ente Photos</title>
|
flex-direction: column;
|
||||||
</head>
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
<body style="background-color: black;">
|
height: 90vh;
|
||||||
<div style="display: flex;
|
"
|
||||||
flex-direction: column;
|
>
|
||||||
align-items: center;
|
<div style="width: 64px">
|
||||||
justify-content: center;
|
<svg
|
||||||
height: 90vh;">
|
version="1.1"
|
||||||
<div style="width:64px;"><svg version="1.1" id="L9" xmlns="http://www.w3.org/2000/svg"
|
id="L9"
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 100"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
enable-background="new 0 0 0 0" xml:space="preserve">
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
<path fill="#2dc262"
|
x="0px"
|
||||||
d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50">
|
y="0px"
|
||||||
<animateTransform attributeName="transform" attributeType="XML" type="rotate" dur="1s"
|
viewBox="0 0 100 100"
|
||||||
from="0 50 50" to="360 50 50" repeatCount="indefinite" />
|
enable-background="new 0 0 0 0"
|
||||||
</path>
|
xml:space="preserve"
|
||||||
</svg>
|
>
|
||||||
|
<path
|
||||||
|
fill="#2dc262"
|
||||||
|
d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50"
|
||||||
|
>
|
||||||
|
<animateTransform
|
||||||
|
attributeName="transform"
|
||||||
|
attributeType="XML"
|
||||||
|
type="rotate"
|
||||||
|
dur="1s"
|
||||||
|
from="0 50 50"
|
||||||
|
to="360 50 50"
|
||||||
|
repeatCount="indefinite"
|
||||||
|
/>
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</body>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Electron Updater Example</title>
|
<title>Electron Updater Example</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
Current version: <span id="version">vX.Y.Z</span>
|
Current version: <span id="version">vX.Y.Z</span>
|
||||||
<div id="messages"></div>
|
<div id="messages"></div>
|
||||||
<script>
|
<script>
|
||||||
// Display the current version
|
// Display the current version
|
||||||
let version = window.location.hash.substring(1);
|
let version = window.location.hash.substring(1);
|
||||||
document.getElementById('version').innerText = version;
|
document.getElementById("version").innerText = version;
|
||||||
|
|
||||||
// Listen for messages
|
// Listen for messages
|
||||||
const {ipcRenderer} = require('electron');
|
const { ipcRenderer } = require("electron");
|
||||||
ipcRenderer.on('message', function(event, text) {
|
ipcRenderer.on("message", function (event, text) {
|
||||||
var container = document.getElementById('messages');
|
var container = document.getElementById("messages");
|
||||||
var message = document.createElement('div');
|
var message = document.createElement("div");
|
||||||
message.innerHTML = text;
|
message.innerHTML = text;
|
||||||
container.appendChild(message);
|
container.appendChild(message);
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
Notes on how to upload electron symbols directly to sentry instance (bypassing the CF limits) cc @abhi just for future reference
|
|
||||||
|
|
||||||
To upload electron symbols
|
|
||||||
|
|
||||||
1. Create a tunnel
|
|
||||||
```
|
|
||||||
ssh -p 7426 -N -L 8080:localhost:9000 sentry
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Add the following env file
|
|
||||||
```
|
|
||||||
NEXT_PUBLIC_IS_SENTRY_ENABLED = yes
|
|
||||||
SENTRY_ORG = ente
|
|
||||||
SENTRY_PROJECT = bhari-frame
|
|
||||||
SENTRY_URL2 = https://sentry.ente.io/
|
|
||||||
SENTRY_URL = http://localhost:8080/
|
|
||||||
SENTRY_AUTH_TOKEN = xxx
|
|
||||||
SENTRY_LOG_LEVEL = debug
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Run
|
|
||||||
|
|
||||||
```
|
|
||||||
node sentry-symbols.js
|
|
||||||
```
|
|
11
desktop/docs/README.md
Normal file
11
desktop/docs/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Developer docs
|
||||||
|
|
||||||
|
If you just want to run the Ente Photos desktop app locally or develop it, you
|
||||||
|
can do:
|
||||||
|
|
||||||
|
yarn install
|
||||||
|
yarn dev
|
||||||
|
|
||||||
|
The docs in this directory provide more details that some developers might find
|
||||||
|
useful. You might also find the developer docs for
|
||||||
|
[web](../../web/docs/README.md) useful.
|
14
desktop/docs/dependencies.md
Normal file
14
desktop/docs/dependencies.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Dependencies
|
||||||
|
|
||||||
|
See [web/docs/dependencies.md](../../web/docs/dependencies.md) for general web
|
||||||
|
specific dependencies. See [electron.md](electron.md) for our main dependency,
|
||||||
|
Electron. The rest of this document describes the remaining, desktop specific
|
||||||
|
dependencies that are used by the Photos desktop app.
|
||||||
|
|
||||||
|
## Electron related
|
||||||
|
|
||||||
|
### next-electron-server
|
||||||
|
|
||||||
|
This spins up a server for serving files using a protocol handler inside our
|
||||||
|
Electron process. This allows us to directly use the output produced by `next
|
||||||
|
build` for loading into our renderer process.
|
4
desktop/docs/dev.md
Normal file
4
desktop/docs/dev.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Development tips
|
||||||
|
|
||||||
|
- `yarn build:quick` is a variant of `yarn build` that uses the
|
||||||
|
`--config.compression=store` flag to (slightly) speed up electron-builder.
|
21
desktop/docs/electron.md
Normal file
21
desktop/docs/electron.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Electron
|
||||||
|
|
||||||
|
[Electron](https://www.electronjs.org) is a cross-platform (Linux, Windows,
|
||||||
|
macOS) way for creating desktop apps using TypeScript.
|
||||||
|
|
||||||
|
Electron embeds Chromium and Node.js in the generated app's binary. The
|
||||||
|
generated app thus consists of two separate processes - the _main_ process, and
|
||||||
|
a _renderer_ process.
|
||||||
|
|
||||||
|
- The _main_ process is runs the embedded node. This process can deal with the
|
||||||
|
host OS - it is conceptually like a `node` repl running on your machine. In our
|
||||||
|
case, the TypeScript code (in the `src/` directory) gets transpiled by `tsc`
|
||||||
|
into JavaScript in the `build/app/` directory, which gets bundled in the
|
||||||
|
generated app's binary and is loaded by the node (main) process when the app
|
||||||
|
starts.
|
||||||
|
|
||||||
|
- The _renderer_ process is a regular web app that gets loaded into the embedded
|
||||||
|
Chromium. When the main process starts, it creates a new "window" that shows
|
||||||
|
this embedded Chromium. In our case, we build and bundle a static export of
|
||||||
|
the [Photos web app](../web/README.md) in the generated app. This gets loaded
|
||||||
|
by the embedded Chromium at runtime, acting as the app's UI.
|
@ -1,10 +1,65 @@
|
|||||||
{
|
{
|
||||||
"name": "ente",
|
"name": "ente",
|
||||||
"productName": "ente",
|
|
||||||
"version": "1.6.63",
|
"version": "1.6.63",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Desktop client for ente.io",
|
"description": "Desktop client for Ente Photos",
|
||||||
|
"author": "Ente <code@ente.io>",
|
||||||
"main": "app/main.js",
|
"main": "app/main.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "yarn build-renderer && yarn build-main",
|
||||||
|
"build-main": "tsc && electron-builder",
|
||||||
|
"build-main:quick": "tsc && electron-builder --config.compression=store",
|
||||||
|
"build-renderer": "cd ../web && yarn install && yarn build:photos && cd ../desktop && rm -f out && ln -sf ../web/apps/photos/out",
|
||||||
|
"build:quick": "yarn build-renderer && yarn build-main:quick",
|
||||||
|
"dev": "concurrently --names 'main,rndr,tscw' \"yarn dev-main\" \"yarn dev-renderer\" \"yarn dev-main-watch\"",
|
||||||
|
"dev-main": "tsc && electron app/main.js",
|
||||||
|
"dev-main-watch": "tsc --watch --preserveWatchOutput",
|
||||||
|
"dev-renderer": "cd ../web && yarn install && yarn dev:photos",
|
||||||
|
"postinstall": "electron-builder install-app-deps",
|
||||||
|
"lint": "yarn prettier --check . && eslint \"src/**/*.ts\"",
|
||||||
|
"lint-fix": "yarn prettier --write . && eslint --fix src"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"any-shell-escape": "^0.1.1",
|
||||||
|
"auto-launch": "^5.0.5",
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
|
"compare-versions": "^6.1.0",
|
||||||
|
"electron-log": "^4.3.5",
|
||||||
|
"electron-reload": "^2.0.0-alpha.1",
|
||||||
|
"electron-store": "^8.0.1",
|
||||||
|
"electron-updater": "^4.3.8",
|
||||||
|
"ffmpeg-static": "^5.1.0",
|
||||||
|
"get-folder-size": "^2.0.1",
|
||||||
|
"html-entities": "^2.4.0",
|
||||||
|
"jpeg-js": "^0.4.4",
|
||||||
|
"next-electron-server": "^1",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"node-stream-zip": "^1.15.0",
|
||||||
|
"onnxruntime-node": "^1.16.3",
|
||||||
|
"promise-fs": "^2.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/auto-launch": "^5.0.2",
|
||||||
|
"@types/ffmpeg-static": "^3.0.1",
|
||||||
|
"@types/get-folder-size": "^2.0.0",
|
||||||
|
"@types/node": "18.15.0",
|
||||||
|
"@types/node-fetch": "^2.6.2",
|
||||||
|
"@types/promise-fs": "^2.1.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
||||||
|
"@typescript-eslint/parser": "^5.28.0",
|
||||||
|
"concurrently": "^7.0.0",
|
||||||
|
"electron": "^25.8.4",
|
||||||
|
"electron-builder": "^24.6.4",
|
||||||
|
"electron-builder-notarize": "^1.2.0",
|
||||||
|
"electron-download": "^4.1.1",
|
||||||
|
"eslint": "^7.23.0",
|
||||||
|
"eslint-config-google": "^0.14.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"prettier": "^3",
|
||||||
|
"prettier-plugin-organize-imports": "^3.2",
|
||||||
|
"prettier-plugin-packagejson": "^2.4",
|
||||||
|
"typescript": "^4.2.3"
|
||||||
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "io.ente.bhari-frame",
|
"appId": "io.ente.bhari-frame",
|
||||||
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
"artifactName": "${productName}-${version}-${arch}.${ext}",
|
||||||
@ -42,7 +97,7 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "./build/icon.icns",
|
"icon": "./resources/icon.icns",
|
||||||
"category": "Photography"
|
"category": "Photography"
|
||||||
},
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
@ -57,98 +112,24 @@
|
|||||||
"x64ArchFiles": "Contents/Resources/ggmlclip-mac"
|
"x64ArchFiles": "Contents/Resources/ggmlclip-mac"
|
||||||
},
|
},
|
||||||
"afterSign": "electron-builder-notarize",
|
"afterSign": "electron-builder-notarize",
|
||||||
"extraFiles": [
|
|
||||||
{
|
|
||||||
"from": "build",
|
|
||||||
"to": "resources",
|
|
||||||
"filter": [
|
|
||||||
"**/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"asarUnpack": [
|
"asarUnpack": [
|
||||||
"node_modules/ffmpeg-static/bin/${os}/${arch}/ffmpeg",
|
"node_modules/ffmpeg-static/bin/${os}/${arch}/ffmpeg",
|
||||||
"node_modules/ffmpeg-static/index.js",
|
"node_modules/ffmpeg-static/index.js",
|
||||||
"node_modules/ffmpeg-static/package.json"
|
"node_modules/ffmpeg-static/package.json"
|
||||||
],
|
],
|
||||||
|
"extraFiles": [
|
||||||
|
{
|
||||||
|
"from": "build",
|
||||||
|
"to": "resources"
|
||||||
|
}
|
||||||
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"app/**/*",
|
"app/**/*",
|
||||||
{
|
"out"
|
||||||
"from": "ui/apps/photos",
|
|
||||||
"to": "ui",
|
|
||||||
"filter": [
|
|
||||||
"!**/*",
|
|
||||||
"out/**/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"productName": "ente",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
|
||||||
"prebuild": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
|
|
||||||
"prepare": "husky install",
|
|
||||||
"lint": "eslint -c .eslintrc --ext .ts src",
|
|
||||||
"watch": "tsc -w",
|
|
||||||
"build-main": "yarn install && tsc",
|
|
||||||
"start-main": "yarn build-main && electron app/main.js",
|
|
||||||
"start-renderer": "cd ui && yarn install && yarn dev:photos",
|
|
||||||
"start": "concurrently \"yarn start-main\" \"yarn start-renderer\"",
|
|
||||||
"build-renderer": "cd ui && yarn install && yarn export:photos",
|
|
||||||
"build": "yarn build-renderer && yarn build-main",
|
|
||||||
"test-release": "cross-env IS_TEST_RELEASE=true yarn build && electron-builder --config.compression=store"
|
|
||||||
},
|
|
||||||
"author": "ente <code@ente.io>",
|
|
||||||
"devDependencies": {
|
|
||||||
"@sentry/cli": "^1.68.0",
|
|
||||||
"@types/auto-launch": "^5.0.2",
|
|
||||||
"@types/ffmpeg-static": "^3.0.1",
|
|
||||||
"@types/get-folder-size": "^2.0.0",
|
|
||||||
"@types/node": "18.15.0",
|
|
||||||
"@types/node-fetch": "^2.6.2",
|
|
||||||
"@types/promise-fs": "^2.1.1",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.28.0",
|
|
||||||
"@typescript-eslint/parser": "^5.28.0",
|
|
||||||
"concurrently": "^7.0.0",
|
|
||||||
"cross-env": "^7.0.3",
|
|
||||||
"electron": "^25.8.4",
|
|
||||||
"electron-builder": "^24.6.4",
|
|
||||||
"electron-builder-notarize": "^1.2.0",
|
|
||||||
"electron-download": "^4.1.1",
|
|
||||||
"eslint": "^7.23.0",
|
|
||||||
"eslint-config-google": "^0.14.0",
|
|
||||||
"eslint-config-prettier": "^8.5.0",
|
|
||||||
"husky": "^8.0.1",
|
|
||||||
"lint-staged": "^13.0.1",
|
|
||||||
"prettier": "2.5.1",
|
|
||||||
"typescript": "^4.2.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@sentry/electron": "^2.5.1",
|
|
||||||
"any-shell-escape": "^0.1.1",
|
|
||||||
"auto-launch": "^5.0.5",
|
|
||||||
"chokidar": "^3.5.3",
|
|
||||||
"compare-versions": "^6.1.0",
|
|
||||||
"electron-log": "^4.3.5",
|
|
||||||
"electron-reload": "^2.0.0-alpha.1",
|
|
||||||
"electron-store": "^8.0.1",
|
|
||||||
"electron-updater": "^4.3.8",
|
|
||||||
"ffmpeg-static": "^5.1.0",
|
|
||||||
"get-folder-size": "^2.0.1",
|
|
||||||
"html-entities": "^2.4.0",
|
|
||||||
"jpeg-js": "^0.4.4",
|
|
||||||
"next-electron-server": "file:./thirdparty/next-electron-server",
|
|
||||||
"node-fetch": "^2.6.7",
|
|
||||||
"node-stream-zip": "^1.15.0",
|
|
||||||
"onnxruntime-node": "^1.16.3",
|
|
||||||
"promise-fs": "^2.1.1"
|
|
||||||
},
|
|
||||||
"standard": {
|
"standard": {
|
||||||
"parser": "babel-eslint"
|
"parser": "babel-eslint"
|
||||||
},
|
|
||||||
"lint-staged": {
|
|
||||||
"src/**/*.{js,jsx,ts,tsx}": [
|
|
||||||
"eslint --fix",
|
|
||||||
"prettier --write --ignore-unknown"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
let SentryCli;
|
|
||||||
let download;
|
|
||||||
|
|
||||||
try {
|
|
||||||
SentryCli = require('@sentry/cli');
|
|
||||||
download = require('electron-download');
|
|
||||||
} catch (e) {
|
|
||||||
console.error('ERROR: Missing required packages, please run:');
|
|
||||||
console.error('npm install --save-dev @sentry/cli electron-download');
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const SYMBOL_CACHE_FOLDER = '.electron-symbols';
|
|
||||||
const sentryCli = new SentryCli('./sentry.properties');
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const version = getElectronVersion();
|
|
||||||
if (!version) {
|
|
||||||
console.error('Cannot detect electron version, check that electron is installed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('We are starting to download all possible electron symbols');
|
|
||||||
console.log('We need it in order to symbolicate native crashes');
|
|
||||||
console.log(
|
|
||||||
'This step is only needed once whenever you update your electron version',
|
|
||||||
);
|
|
||||||
console.log('Just call this script again it should do everything for you.');
|
|
||||||
|
|
||||||
let zipPath = await downloadSymbols({
|
|
||||||
version,
|
|
||||||
platform: 'darwin',
|
|
||||||
arch: 'x64',
|
|
||||||
dsym: true,
|
|
||||||
});
|
|
||||||
await sentryCli.execute(['upload-dif', '-t', 'dsym', zipPath], true);
|
|
||||||
|
|
||||||
zipPath = await downloadSymbols({
|
|
||||||
version,
|
|
||||||
platform: 'win32',
|
|
||||||
arch: 'ia32',
|
|
||||||
symbols: true,
|
|
||||||
});
|
|
||||||
await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);
|
|
||||||
|
|
||||||
zipPath = await downloadSymbols({
|
|
||||||
version,
|
|
||||||
platform: 'win32',
|
|
||||||
arch: 'x64',
|
|
||||||
symbols: true,
|
|
||||||
});
|
|
||||||
await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);
|
|
||||||
|
|
||||||
zipPath = await downloadSymbols({
|
|
||||||
version,
|
|
||||||
platform: 'linux',
|
|
||||||
arch: 'x64',
|
|
||||||
symbols: true,
|
|
||||||
});
|
|
||||||
await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);
|
|
||||||
|
|
||||||
console.log('Finished downloading and uploading to Sentry');
|
|
||||||
console.log(`Feel free to delete the ${SYMBOL_CACHE_FOLDER}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getElectronVersion() {
|
|
||||||
try {
|
|
||||||
return require('electron/package.json').version;
|
|
||||||
} catch (error) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function downloadSymbols(options) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
download(
|
|
||||||
{
|
|
||||||
...options,
|
|
||||||
cache: SYMBOL_CACHE_FOLDER,
|
|
||||||
},
|
|
||||||
(err, zipPath) => {
|
|
||||||
if (err) {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
resolve(zipPath);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
main().catch(e => console.error(e));
|
|
@ -1,3 +0,0 @@
|
|||||||
defaults.url=https://sentry.ente.io/
|
|
||||||
defaults.org=ente
|
|
||||||
defaults.project=desktop-photos
|
|
@ -1,16 +1,16 @@
|
|||||||
import { ipcRenderer } from 'electron/renderer';
|
import { ipcRenderer } from "electron/renderer";
|
||||||
import path from 'path';
|
import path from "path";
|
||||||
import { existsSync, mkdir, rmSync } from 'promise-fs';
|
import { existsSync, mkdir, rmSync } from "promise-fs";
|
||||||
import { DiskCache } from '../services/diskCache';
|
import { DiskCache } from "../services/diskCache";
|
||||||
|
|
||||||
const ENTE_CACHE_DIR_NAME = 'ente';
|
const ENTE_CACHE_DIR_NAME = "ente";
|
||||||
|
|
||||||
export const getCacheDirectory = async () => {
|
export const getCacheDirectory = async () => {
|
||||||
const customCacheDir = await getCustomCacheDirectory();
|
const customCacheDir = await getCustomCacheDirectory();
|
||||||
if (customCacheDir && existsSync(customCacheDir)) {
|
if (customCacheDir && existsSync(customCacheDir)) {
|
||||||
return customCacheDir;
|
return customCacheDir;
|
||||||
}
|
}
|
||||||
const defaultSystemCacheDir = await ipcRenderer.invoke('get-path', 'cache');
|
const defaultSystemCacheDir = await ipcRenderer.invoke("get-path", "cache");
|
||||||
return path.join(defaultSystemCacheDir, ENTE_CACHE_DIR_NAME);
|
return path.join(defaultSystemCacheDir, ENTE_CACHE_DIR_NAME);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ const getCacheBucketDir = async (cacheName: string) => {
|
|||||||
|
|
||||||
export async function openDiskCache(
|
export async function openDiskCache(
|
||||||
cacheName: string,
|
cacheName: string,
|
||||||
cacheLimitInBytes?: number
|
cacheLimitInBytes?: number,
|
||||||
) {
|
) {
|
||||||
const cacheBucketDir = await getCacheBucketDir(cacheName);
|
const cacheBucketDir = await getCacheBucketDir(cacheName);
|
||||||
if (!existsSync(cacheBucketDir)) {
|
if (!existsSync(cacheBucketDir)) {
|
||||||
@ -42,11 +42,11 @@ export async function deleteDiskCache(cacheName: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function setCustomCacheDirectory(
|
export async function setCustomCacheDirectory(
|
||||||
directory: string
|
directory: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await ipcRenderer.invoke('set-custom-cache-directory', directory);
|
await ipcRenderer.invoke("set-custom-cache-directory", directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCustomCacheDirectory(): Promise<string> {
|
async function getCustomCacheDirectory(): Promise<string> {
|
||||||
return await ipcRenderer.invoke('get-custom-cache-directory');
|
return await ipcRenderer.invoke("get-custom-cache-directory");
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from "electron";
|
||||||
import { writeStream } from '../services/fs';
|
import { writeStream } from "../services/fs";
|
||||||
import { isExecError } from '../utils/error';
|
import { Model } from "../types";
|
||||||
import { parseExecError } from '../utils/error';
|
import { isExecError, parseExecError } from "../utils/error";
|
||||||
import { Model } from '../types';
|
|
||||||
|
|
||||||
export async function computeImageEmbedding(
|
export async function computeImageEmbedding(
|
||||||
model: Model,
|
model: Model,
|
||||||
imageData: Uint8Array
|
imageData: Uint8Array,
|
||||||
): Promise<Float32Array> {
|
): Promise<Float32Array> {
|
||||||
let tempInputFilePath = null;
|
let tempInputFilePath = null;
|
||||||
try {
|
try {
|
||||||
tempInputFilePath = await ipcRenderer.invoke('get-temp-file-path', '');
|
tempInputFilePath = await ipcRenderer.invoke("get-temp-file-path", "");
|
||||||
const imageStream = new Response(imageData.buffer).body;
|
const imageStream = new Response(imageData.buffer).body;
|
||||||
await writeStream(tempInputFilePath, imageStream);
|
await writeStream(tempInputFilePath, imageStream);
|
||||||
const embedding = await ipcRenderer.invoke(
|
const embedding = await ipcRenderer.invoke(
|
||||||
'compute-image-embedding',
|
"compute-image-embedding",
|
||||||
model,
|
model,
|
||||||
tempInputFilePath
|
tempInputFilePath,
|
||||||
);
|
);
|
||||||
return embedding;
|
return embedding;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -28,20 +27,20 @@ export async function computeImageEmbedding(
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (tempInputFilePath) {
|
if (tempInputFilePath) {
|
||||||
await ipcRenderer.invoke('remove-temp-file', tempInputFilePath);
|
await ipcRenderer.invoke("remove-temp-file", tempInputFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function computeTextEmbedding(
|
export async function computeTextEmbedding(
|
||||||
model: Model,
|
model: Model,
|
||||||
text: string
|
text: string,
|
||||||
): Promise<Float32Array> {
|
): Promise<Float32Array> {
|
||||||
try {
|
try {
|
||||||
const embedding = await ipcRenderer.invoke(
|
const embedding = await ipcRenderer.invoke(
|
||||||
'compute-text-embedding',
|
"compute-text-embedding",
|
||||||
model,
|
model,
|
||||||
text
|
text,
|
||||||
);
|
);
|
||||||
return embedding;
|
return embedding;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1,44 +1,39 @@
|
|||||||
import { ipcRenderer } from 'electron/renderer';
|
import { ipcRenderer } from "electron/renderer";
|
||||||
import { logError } from '../services/logging';
|
import { logError } from "../services/logging";
|
||||||
|
|
||||||
export const selectDirectory = async (): Promise<string> => {
|
export const selectDirectory = async (): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
return await ipcRenderer.invoke('select-dir');
|
return await ipcRenderer.invoke("select-dir");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'error while selecting root directory');
|
logError(e, "error while selecting root directory");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAppVersion = async (): Promise<string> => {
|
export const getAppVersion = async (): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
return await ipcRenderer.invoke('get-app-version');
|
return await ipcRenderer.invoke("get-app-version");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'failed to get release version');
|
logError(e, "failed to get release version");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const openDirectory = async (dirPath: string): Promise<void> => {
|
export const openDirectory = async (dirPath: string): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await ipcRenderer.invoke('open-dir', dirPath);
|
await ipcRenderer.invoke("open-dir", dirPath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'error while opening directory');
|
logError(e, "error while opening directory");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPlatform = async (): Promise<'mac' | 'windows' | 'linux'> => {
|
export const getPlatform = async (): Promise<"mac" | "windows" | "linux"> => {
|
||||||
try {
|
try {
|
||||||
return await ipcRenderer.invoke('get-platform');
|
return await ipcRenderer.invoke("get-platform");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'failed to get platform');
|
logError(e, "failed to get platform");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export { logToDisk, openLogDirectory } from "../services/logging";
|
||||||
logToDisk,
|
|
||||||
openLogDirectory,
|
|
||||||
getSentryUserID,
|
|
||||||
updateOptOutOfCrashReports,
|
|
||||||
} from '../services/logging';
|
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { keysStore } from '../stores/keys.store';
|
import { logError } from "../services/logging";
|
||||||
import { safeStorageStore } from '../stores/safeStorage.store';
|
import { keysStore } from "../stores/keys.store";
|
||||||
import { uploadStatusStore } from '../stores/upload.store';
|
import { safeStorageStore } from "../stores/safeStorage.store";
|
||||||
import { logError } from '../services/logging';
|
import { uploadStatusStore } from "../stores/upload.store";
|
||||||
import { userPreferencesStore } from '../stores/userPreferences.store';
|
import { watchStore } from "../stores/watch.store";
|
||||||
import { watchStore } from '../stores/watch.store';
|
|
||||||
|
|
||||||
export const clearElectronStore = () => {
|
export const clearElectronStore = () => {
|
||||||
try {
|
try {
|
||||||
@ -11,9 +10,8 @@ export const clearElectronStore = () => {
|
|||||||
keysStore.clear();
|
keysStore.clear();
|
||||||
safeStorageStore.clear();
|
safeStorageStore.clear();
|
||||||
watchStore.clear();
|
watchStore.clear();
|
||||||
userPreferencesStore.delete('optOutOfCrashReports');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'error while clearing electron store');
|
logError(e, "error while clearing electron store");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user