Compare commits

..
Author SHA1 Message Date
Torsten Dittmann 2a3762cc24 Merge branch 'main' of https://github.com/appwrite/appwrite into feat-ghcr 2024-07-18 21:25:22 +02:00
Christy JacobandGitHub 483ef3c00b Merge pull request #8397 from appwrite/feat-xdebug-profiling
feat: Adding profiler for debugging
2024-07-15 22:19:08 +04:00
Binyamin Yawitz eacd965ae3 feat: Adding profiler for debugging 2024-07-15 14:13:34 -04:00
Steven NguyenandGitHub f3ad9a3f81 Merge pull request #7501 from Achraf112Ben/adding-darija-moroccan-arabic
Adding Darija (Moroccan Arabic) translation file
2024-07-14 21:39:20 -07:00
achraf112ben 3a35412bf8 fix some missed values 2024-01-29 04:21:14 +01:00
achraf112ben 4f67db160b Adding Darija (Moroccan Arabic) translation file 2024-01-28 06:14:21 +01:00
Torsten Dittmann 3eb79cc271 ci: update actions 2023-11-06 12:21:19 +01:00
Torsten Dittmann 3e5e14e4c8 ci: use ghcr.io 2023-11-06 12:21:19 +01:00
Torsten Dittmann 7629c184ac feat: use ghcr.io 2023-11-06 12:21:19 +01:00
4980 changed files with 766 additions and 80467 deletions
+3 -3
View File
@@ -4,7 +4,6 @@ _APP_LOCALE=en
_APP_WORKER_PER_CORE=6
_APP_CONSOLE_WHITELIST_ROOT=disabled
_APP_CONSOLE_WHITELIST_EMAILS=
_APP_CONSOLE_SESSION_ALERTS=enabled
_APP_CONSOLE_WHITELIST_IPS=
_APP_CONSOLE_COUNTRIES_DENYLIST=AQ
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
@@ -18,7 +17,7 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
_APP_DOMAIN=traefik
_APP_DOMAIN=localhost
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_TARGET=localhost
_APP_REDIS_HOST=redis
@@ -86,6 +85,7 @@ _APP_USAGE_AGGREGATION_INTERVAL=30
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_USAGE_STATS=enabled
_APP_LOGGING_PROVIDER=
_APP_LOGGING_CONFIG=
_APP_GRAPHQL_MAX_BATCH_SIZE=10
_APP_GRAPHQL_MAX_COMPLEXITY=250
@@ -105,4 +105,4 @@ _APP_MESSAGE_SMS_TEST_DSN=
_APP_MESSAGE_EMAIL_TEST_DSN=
_APP_MESSAGE_PUSH_TEST_DSN=
_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10
_APP_PROJECT_REGIONS=default
_APP_PROJECT_REGIONS=default
+17 -9
View File
@@ -4,14 +4,21 @@ on:
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
tests:
name: Release
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -20,29 +27,30 @@ jobs:
submodules: recursive
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: appwrite/appwrite
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{major}}.{{minor}}.{{patch}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push
uses: docker/build-push-action@v4
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
+1 -1
View File
@@ -1,4 +1,4 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
branch = 1.6.x
branch = 4.3.14
+12
View File
@@ -497,6 +497,18 @@ If you are in PHP Storm you don't need any plugin. Below are the settings requir
2. If needed edit the **dev/xdebug.ini** file to your needs.
3. Launch your Appwrite instance while your debugger is listening for connections.
## Profiling
Appwrite uses XDebug [Profiler](https://xdebug.org/docs/profiler) for generating **CacheGrind** files. The generated file would be located in each of the `appwrite` containers inside the `/tmp/xdebug` folder.
To disable the profiler while debugging remove the `,profiler` mode from the `xdebug.ini` file
```diff
zend_extension=xdebug
[xdebug]
-xdebug.mode=develop,debug,profile
+xdebug.mode=develop,debug
```
### VS Code Launch Configuration
```json
+2 -2
View File
@@ -79,7 +79,6 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/migrate && \
chmod +x /usr/local/bin/realtime && \
chmod +x /usr/local/bin/schedule-functions && \
chmod +x /usr/local/bin/schedule-executions && \
chmod +x /usr/local/bin/schedule-messages && \
chmod +x /usr/local/bin/sdks && \
chmod +x /usr/local/bin/specs && \
@@ -109,9 +108,10 @@ RUN mkdir -p /etc/letsencrypt/live/ && chmod -Rf 755 /etc/letsencrypt/live/
# Enable Extensions
RUN if [ "$DEBUG" == "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" == "true" ]; then mkdir -p /tmp/xdebug; fi
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/lib/php/extensions/no-debug-non-zts-20220829/xdebug.so; fi
EXPOSE 80
CMD [ "php", "app/http.php" ]
CMD [ "php", "app/http.php" ]
+2 -56
View File
@@ -3029,7 +3029,7 @@ $projectCollections = array_merge([
'size' => 8,
'signed' => true,
'required' => false,
'default' => 'v4',
'default' => 'v3',
'array' => false,
'filters' => [],
],
@@ -3054,18 +3054,7 @@ $projectCollections = array_merge([
'required' => false,
'default' => null,
'filters' => [],
],
[
'$id' => ID::custom('scopes'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => true,
'filters' => [],
],
]
],
'indexes' => [
[
@@ -3878,27 +3867,6 @@ $projectCollections = array_merge([
'lengths' => [32],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_requestMethod'),
'type' => Database::INDEX_KEY,
'attributes' => ['requestMethod'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_requestPath'),
'type' => Database::INDEX_KEY,
'attributes' => ['requestPath'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_deployment'),
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_responseStatusCode'),
'type' => Database::INDEX_KEY,
@@ -4343,17 +4311,6 @@ $consoleCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => 'accessedAt',
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('services'),
'type' => Database::VAR_STRING,
@@ -4561,17 +4518,6 @@ $consoleCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('data'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 65535,
'signed' => true,
'required' => false,
'default' => new \stdClass(),
'array' => false,
'filters' => ['json', 'encrypt'],
],
[
'$id' => ID::custom('active'),
'type' => Database::VAR_BOOLEAN,
-16
View File
@@ -332,11 +332,6 @@ return [
'description' => 'API key and session used in the same request. Use either `setSession` or `setKey`. Learn about which authentication method to use in the SSR docs: https://appwrite.io/docs/products/auth/server-side-rendering',
'code' => 403,
],
Exception::API_KEY_EXPIRED => [
'name' => Exception::API_KEY_EXPIRED,
'description' => 'The dynamic API key has expired. Please don\'t use dynamic API keys for more than duration of the execution.',
'code' => 401,
],
/** Teams */
Exception::TEAM_NOT_FOUND => [
@@ -546,11 +541,6 @@ return [
'description' => 'Build with the requested ID is already in progress. Please wait before you can retry.',
'code' => 400,
],
Exception::BUILD_ALREADY_COMPLETED => [
'name' => Exception::BUILD_ALREADY_COMPLETED,
'description' => 'Build with the requested ID is already completed and cannot be canceled.',
'code' => 400,
],
/** Deployments */
Exception::DEPLOYMENT_NOT_FOUND => [
@@ -566,12 +556,6 @@ return [
'code' => 404,
],
Exception::EXECUTION_IN_PROGRESS => [
'name' => Exception::EXECUTION_IN_PROGRESS,
'description' => 'Can\'t delete ongoing execution. Please wait for execution to finish before deleting it.',
'code' => 400,
],
/** Databases */
Exception::DATABASE_NOT_FOUND => [
'name' => Exception::DATABASE_NOT_FOUND,
@@ -1,14 +0,0 @@
<p>{{hello}},</p>
<p>{{body}}</p>
<ol>
<li>{{listDevice}}</li>
<li>{{listIpAddress}}</li>
<li>{{listCountry}}</li>
</ol>
<p>{{footer}}</p>
<p style="margin-bottom: 0px;">{{thanks}}</p>
<p style="margin-top: 0px;">{{signature}}</p>
+238
View File
@@ -0,0 +1,238 @@
{
"settings.inspire": "\"الفن ديال الحكمة هو الفن ديال أنك تعرف أش تنخّل.\"",
"settings.locale": "ar-ma",
"settings.direction": "rtl",
"emails.sender": "فرقة %s",
"emails.verification.subject": "التيْقان ديال الحساب",
"emails.verification.hello": "السلام {{user}}",
"emails.verification.body": "تبّع هاد الوصلة باش تيقّن لادريسة تاع ليميل ديالك.",
"emails.verification.footer": "إلا ماشي نتا اللي طلبتي تيقّن هاد لادريسة تاع ليميل، ممكن تنخّل هاد البرية.",
"emails.verification.thanks": "شكرا",
"emails.verification.signature": "فرقة {{project}}",
"emails.magicSession.subject": "تكونيكطا",
"emails.magicSession.hello": "السلام,",
"emails.magicSession.body": "تبّع هاد الوصلة باش تتكونيكطا.",
"emails.magicSession.footer": "إلا ماشي نتا اللي طلبتي تتكونيكطا بهاد ليميل، ممكن تنخّل هاد البرية.",
"emails.magicSession.thanks": "شكرا",
"emails.magicSession.signature": "فرقة {{project}}",
"emails.recovery.subject": "تبدال كلمة السر",
"emails.recovery.hello": "السلام {{user}}",
"emails.recovery.body": "تبّع هاد الوصلة باش تبدّل كلمة السر تاع {{project}}.",
"emails.recovery.footer": "إلا ماشي نتا اللي طلبتي تبدّل كلمة السر، ممكن تنخّل هاد البرية.",
"emails.recovery.thanks": "شكرا",
"emails.recovery.signature": "فرقة {{project}}",
"emails.invitation.subject": "عراضة ل فرقة %s ف %s",
"emails.invitation.hello": "السلام",
"emails.invitation.body": "هاد البرية تصيفطات ليك حيت {{owner}} بغى يعرض عليك تولّي عضو ف فرقة {{team}} عند {{project}}.",
"emails.invitation.footer": "إلا كنتي ما مسوّقش, ممكن تنخّل هاد البرية.",
"emails.invitation.thanks": "شكرا",
"emails.invitation.signature": "فرقة {{project}}",
"emails.certificate.subject": "السرتافيكة فشلات ل %s",
"emails.certificate.hello": "السلام",
"emails.certificate.body": "السرتافيكة ديال الضومين ديالك '{{domain}}' ما قدّاتش تجينيرا. هادي هي المحاولة نمرة {{attempt}}, السبب ديال هاد الفشل هو: {{error}}",
"emails.certificate.footer": "السرتافيكة الفايتة ديالك غاتبقى مزيانة لمدة 30 يوم من عند أول فشل. كانشجعوك بزاف أنك تبقشش فهاد الموضوع, وا إلّا الضومين ديالك ما غايبقاش خدّام فيه الـ SSL.",
"emails.certificate.thanks": "شكرا",
"emails.certificate.signature": "فرقة {{project}}",
"locale.country.unknown": "ما معروفش",
"countries.af": "أفغانستان",
"countries.ao": "أنڭولا",
"countries.al": "ألبانيا",
"countries.ad": "أندورا",
"countries.ae": "الإمارات العربية المتّاحدة",
"countries.ar": "الأرجنتين",
"countries.am": "أرمينيا",
"countries.ag": "أنتيڭوا وبربودا",
"countries.au": "ؤسطراليا",
"countries.at": "النامسا",
"countries.az": "أديربيجان",
"countries.bi": "بوروندي",
"countries.be": "بلجيكا",
"countries.bj": "بينين",
"countries.bf": "بوركينا فاصو",
"countries.bd": "بنڭلاديش",
"countries.bg": "بلڭاريا",
"countries.bh": "البحرين",
"countries.bs": "دزيرات البهاما",
"countries.ba": "البوسنة ؤ الهرسك",
"countries.by": "بيلاروسيا",
"countries.bz": "بيليز",
"countries.bo": "بوليڤيا",
"countries.br": "البرازيل",
"countries.bb": "باربادوس",
"countries.bn": "بروناي",
"countries.bt": "بوتان",
"countries.bw": "بوتسوانا",
"countries.cf": "جمهورية إفريقيا الوسطانية",
"countries.ca": "كانادا",
"countries.ch": "سويسرا",
"countries.cl": "تشيلي",
"countries.cn": "الشينوا",
"countries.ci": "ساحل العاج",
"countries.cm": "الكاميرون",
"countries.cd": "جمهورية الكونڭو الديمقراطية",
"countries.cg": "جمهورية الكونڭو",
"countries.co": "كولومبيا",
"countries.km": "دزيرات القومور",
"countries.cv": "الراس الخضر",
"countries.cr": "كوسطاريكا",
"countries.cu": "كوبا",
"countries.cy": "قوبروص",
"countries.cz": "التشيك",
"countries.de": "ألمانيا",
"countries.dj": "دجيبوتي",
"countries.dm": "ضومينيكا",
"countries.dk": "الدنمارك",
"countries.do": "جمهورية الضومينيكان",
"countries.dz": "الدزاير",
"countries.ec": "إكوادور",
"countries.eg": "مصر",
"countries.er": "إريتريا",
"countries.es": "سبانيا",
"countries.ee": "إسطونيا",
"countries.et": "إتيوپيا",
"countries.fi": "فينلاندا",
"countries.fj": "فيدجي",
"countries.fr": "فرانسا",
"countries.fm": "ميكرونيزيا",
"countries.ga": "الڭابون",
"countries.gb": "المملكة المتّاحدة",
"countries.ge": "تجورجيا",
"countries.gh": "غانا",
"countries.gn": "غينيا",
"countries.gm": "ڭامبيا",
"countries.gw": "غينيا بيساو",
"countries.gq": "غينيا الستوائية",
"countries.gr": "اليونان",
"countries.gd": "ڭرينادا",
"countries.gt": "ڭواتيمالا",
"countries.gy": "ڭيانا",
"countries.hn": "هوندوراس",
"countries.hr": "كرواتيا",
"countries.ht": "هايتي",
"countries.hu": "الماجر",
"countries.id": "إندونيسيا",
"countries.in": "الهند",
"countries.ie": "إرلاندا",
"countries.ir": "إران",
"countries.iq": "العراق",
"countries.is": "إسلاندا",
"countries.il": "إسرائيل",
"countries.it": "الطاليان",
"countries.jm": "جامايكا",
"countries.jo": "الأردن",
"countries.jp": "الجاپون",
"countries.kz": "كازاخستان",
"countries.ke": "كينيا",
"countries.kg": "قيرغيزستان",
"countries.kh": "كمبوديا",
"countries.ki": "كيريباتي",
"countries.kn": "سانت كيتس ؤ نيفيس",
"countries.kr": "كوريا الجنوبية",
"countries.kw": "الكويت",
"countries.la": "لاوس",
"countries.lb": "لبنان",
"countries.lr": "ليبيريا",
"countries.ly": "ليبيا",
"countries.lc": "سانت لوسيا",
"countries.li": "ليختنشتاين",
"countries.lk": "سري لانكا",
"countries.ls": "ليسوتو",
"countries.lt": "ليتوانيا",
"countries.lu": "لوكسمبورڭ",
"countries.lv": "لاتفيا",
"countries.ma": "المغريب",
"countries.mc": "موناكو",
"countries.md": "مولضوڤا",
"countries.mg": "ماداغشقار",
"countries.mv": "دزيرات المالديڤ",
"countries.mx": "الميكسيك",
"countries.mh": "دزيرات مارشال",
"countries.mk": "مقدونيا",
"countries.ml": "مالي",
"countries.mt": "مالطا",
"countries.mm": "ميانمار",
"countries.me": "مونطينيڭرو",
"countries.mn": "منغوليا",
"countries.mz": "الموزمبيق",
"countries.mr": "موريتانيا",
"countries.mu": "موريشيوس",
"countries.mw": "مالاوي",
"countries.my": "ماليزيا",
"countries.na": "ناميبيا",
"countries.ne": "النيجر",
"countries.ng": "نيجيريا",
"countries.ni": "نيكاراڭوا",
"countries.nl": "هولاندا",
"countries.no": "النرويج",
"countries.np": "نيپال",
"countries.nr": "ناورو",
"countries.nz": "نيوزيلاندا",
"countries.om": "عمّان",
"countries.pk": "پاكيستان",
"countries.pa": "پاناما",
"countries.pe": "الپيرو",
"countries.ph": "الفيليپين",
"countries.pw": "پالاو",
"countries.pg": "پاپوا غينيا الجديدة",
"countries.pl": "پولاندا",
"countries.kp": "كوريا الشمالية",
"countries.pt": "البرطقيز",
"countries.py": "الپاراڭواي",
"countries.qa": "قطر",
"countries.ro": "رومانيا",
"countries.ru": "روسيا",
"countries.rw": "روّاندا",
"countries.sa": "المملكة العربية السعودية",
"countries.sd": "السودان",
"countries.sn": "السينيڭال",
"countries.sg": "سنغافورة",
"countries.sb": "دزيرات سليمان",
"countries.sl": "صييراليون",
"countries.sv": "السالڤاضور",
"countries.sm": "سان مارينو",
"countries.so": "الصومال",
"countries.rs": "صيربيا",
"countries.ss": "جنوب السودان",
"countries.st": "صاو طومي ؤ پرينسيپي",
"countries.sr": "سورينام",
"countries.sk": "صلوڤاكيا",
"countries.si": "صلوڤينيا",
"countries.se": "السويد",
"countries.sz": "سوازيلاند",
"countries.sc": "السيشيل",
"countries.sy": "سوريا",
"countries.td": "تشاد",
"countries.tg": "الطوڭو",
"countries.th": "الطايلوند",
"countries.tj": "طادجيكيستان",
"countries.tm": "تركمانيستان",
"countries.tl": "تيمور الشرقية",
"countries.to": "تونڭا",
"countries.tt": "ترينيداد ؤ طوباڭو",
"countries.tn": "تونس",
"countries.tr": "توركيا",
"countries.tv": "توڤالو",
"countries.tz": "طنزانيا",
"countries.ug": "ؤڭاندا",
"countries.ua": "ؤكرانيا",
"countries.uy": "ؤروڭواي",
"countries.us": "ميريكان",
"countries.uz": "ؤزباكيستان",
"countries.va": "مدينة الڤاتيكان",
"countries.vc": "سانت ڤانسون ؤ دزيرات ڭرينادين",
"countries.ve": "ڤينيزويلا",
"countries.vn": "ڤيطنام",
"countries.vu": "ڤانواتو",
"countries.ws": "ساموا",
"countries.ye": "اليمن",
"countries.za": "جنوب إفريقيا",
"countries.zm": "زامبيا",
"countries.zw": "زيمبابوي",
"continents.af": "أفريقيا",
"continents.an": "القارة القطبية الجنوبية",
"continents.as": "أسيا",
"continents.eu": "ؤروپا",
"continents.na": "ميريكان الشمالية",
"continents.oc": "ؤقيانوسيا",
"continents.sa": "ميريكان الجنوبية"
}
+1 -10
View File
@@ -18,15 +18,6 @@
"emails.magicSession.securityPhrase": "Security phrase for this email is {{b}}{{phrase}}{{/b}}. You can trust this email if this phrase matches the phrase shown during sign in.",
"emails.magicSession.thanks": "Thanks,",
"emails.magicSession.signature": "{{project}} team",
"emails.sessionAlert.subject": "New session alert for {{project}}",
"emails.sessionAlert.hello":"Hello {{user}}",
"emails.sessionAlert.body": "We're writing to inform you that a new session has been initiated on your {{b}}{{project}}{{/b}} account, on {{b}}{{dateTime}}{{/b}}. \nHere are the details of the new session: ",
"emails.sessionAlert.listDevice": "Device: {{b}}{{device}}{{/b}}",
"emails.sessionAlert.listIpAddress": "IP Address: {{b}}{{ipAddress}}{{/b}}",
"emails.sessionAlert.listCountry": "Country: {{b}}{{country}}{{/b}}",
"emails.sessionAlert.footer": "If you didn't request the sign in, you can safely ignore this email. If you suspect unauthorized activity, please secure your account immediately.",
"emails.sessionAlert.thanks": "Thanks,",
"emails.sessionAlert.signature": "{{project}} team",
"emails.otpSession.subject": "OTP for {{project}} Login",
"emails.otpSession.hello": "Hello {{user}}",
"emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.",
@@ -43,7 +34,7 @@
"emails.recovery.subject": "Password Reset",
"emails.recovery.hello": "Hello {{user}}",
"emails.recovery.body": "Follow this link to reset your {{b}}{{project}}{{/b}} password.",
"emails.recovery.footer": "If you didn't ask to reset your password, you can ignore this message.",
"emails.recovery.footer": "If you didnt ask to reset your password, you can ignore this message.",
"emails.recovery.thanks": "Thanks",
"emails.recovery.signature": "{{project}} team",
"emails.invitation.subject": "Invitation to %s Team at %s",
+7 -7
View File
@@ -15,7 +15,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '16.0.0-rc.1',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -63,7 +63,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '13.0.0-rc.1',
'version' => '12.0.4',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -221,7 +221,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '6.0.0-rc.3',
'version' => '5.0.5',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -249,7 +249,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '14.0.0-rc.1',
'version' => '13.0.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -341,8 +341,8 @@ return [
'name' => 'Go',
'version' => '4.0.1',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
'package' => '',
'enabled' => false,
'beta' => true,
'dev' => false,
'hidden' => false,
@@ -393,7 +393,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '12.0.0-rc.1',
'version' => '11.0.3',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
+1 -1
View File
@@ -6,4 +6,4 @@
use Appwrite\Runtimes\Runtimes;
return (new Runtimes('v4'))->getAll();
return (new Runtimes('v3'))->getAll();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -11
View File
@@ -198,7 +198,7 @@ return [
],
[
'name' => '_APP_LOGGING_PROVIDER',
'description' => 'Deprecated since 1.6.0, use `_APP_LOGGING_CONFIG` with DSN value instead. This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, set the value to one of \'sentry\', \'raygun\', \'appSignal\', \'logOwl\' to enable the logger.',
'description' => 'This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, set the value to one of \'sentry\', \'raygun\', \'appSignal\', \'logOwl\' to enable the logger.',
'introduction' => '0.12.0',
'default' => '',
'required' => false,
@@ -207,7 +207,7 @@ return [
],
[
'name' => '_APP_LOGGING_CONFIG',
'description' => 'This variable allows you to enable logging errors to third party providers. This value is empty by default, set a DSN value to one of the following `sentry://PROJECT_ID:SENTRY_API_KEY@SENTRY_HOST/`, , `logowl://SERVICE_TICKET@SERIVCE_HOST/` `raygun://RAYGUN_API_KEY/`, `appSignal://API_KEY/` to enable the logger.\n\nFor versions prior `1.5.6` you can use the old syntax.\n\nOld syntax: If using Sentry, this should be \'SENTRY_API_KEY;SENTRY_APP_ID\'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket.',
'description' => 'This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be \'SENTRY_API_KEY;SENTRY_APP_ID\'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket.',
'introduction' => '0.12.0',
'default' => '',
'required' => false,
@@ -250,15 +250,6 @@ return [
'question' => '',
'filter' => ''
],
[
'name' => '_APP_CONSOLE_SESSION_ALERTS',
'description' => 'This option allows you configure if a new login in the Appwrite Console should send an alert email to the user. It\'s disabled by default with value "disabled", and to enable it, pass value "enabled".',
'introduction' => '1.6.0',
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
[
+95 -506
View File
@@ -5,7 +5,6 @@ use Appwrite\Auth\Auth;
use Appwrite\Auth\MFA\Challenge;
use Appwrite\Auth\MFA\Type;
use Appwrite\Auth\MFA\Type\TOTP;
use Appwrite\Auth\MFA\Type\WebAuthn;
use Appwrite\Auth\OAuth2\Exception as OAuth2Exception;
use Appwrite\Auth\Phrase;
use Appwrite\Auth\Validator\Password;
@@ -59,92 +58,7 @@ use Utopia\Validator\WhiteList;
$oauthDefaultSuccess = '/auth/oauth2/success';
$oauthDefaultFailure = '/auth/oauth2/failure';
function sendSessionAlert(Locale $locale, Document $user, Document $project, Document $session, Mail $queueForMails)
{
$subject = $locale->getText("emails.sessionAlert.subject");
$customTemplate = $project->getAttribute('templates', [])['email.sessionAlert-' . $locale->default] ?? [];
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-session-alert.tpl');
$message
->setParam('{{hello}}', $locale->getText("emails.sessionAlert.hello"))
->setParam('{{body}}', $locale->getText("emails.sessionAlert.body"))
->setParam('{{listDevice}}', $locale->getText("emails.sessionAlert.listDevice"))
->setParam('{{listIpAddress}}', $locale->getText("emails.sessionAlert.listIpAddress"))
->setParam('{{listCountry}}', $locale->getText("emails.sessionAlert.listCountry"))
->setParam('{{footer}}', $locale->getText("emails.sessionAlert.footer"))
->setParam('{{thanks}}', $locale->getText("emails.sessionAlert.thanks"))
->setParam('{{signature}}', $locale->getText("emails.sessionAlert.signature"));
$body = $message->render();
$smtp = $project->getAttribute('smtp', []);
$smtpEnabled = $smtp['enabled'] ?? false;
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
$senderEmail = $smtp['senderEmail'];
}
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
}
$queueForMails
->setSmtpHost($smtp['host'] ?? '')
->setSmtpPort($smtp['port'] ?? '')
->setSmtpUsername($smtp['username'] ?? '')
->setSmtpPassword($smtp['password'] ?? '')
->setSmtpSecure($smtp['secure'] ?? '');
if (!empty($customTemplate)) {
if (!empty($customTemplate['senderEmail'])) {
$senderEmail = $customTemplate['senderEmail'];
}
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
}
$body = $customTemplate['message'] ?? '';
$subject = $customTemplate['subject'] ?? $subject;
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
$emailVariables = [
'direction' => $locale->getText('settings.direction'),
'dateTime' => DateTime::format(new \DateTime(), 'Y-m-d H:i:s'),
'user' => $user->getAttribute('name'),
'project' => $project->getAttribute('name'),
'device' => $session->getAttribute('clientName'),
'ipAddress' => $session->getAttribute('ip'),
'country' => $locale->getText('countries.' . $session->getAttribute('countryCode'), $locale->getText('locale.country.unknown')),
];
$email = $user->getAttribute('email');
$queueForMails
->setSubject($subject)
->setBody($body)
->setVariables($emailVariables)
->setRecipient($email)
->trigger();
};
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) {
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@@ -224,10 +138,6 @@ $createSession = function (string $userId, string $secret, Request $request, Res
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB');
}
if ($project->getAttribute('auths', [])['sessionAlerts'] ?? false) {
sendSessionAlert($locale, $user, $project, $session, $queueForMails);
}
$queueForEvents
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId());
@@ -471,6 +381,7 @@ App::delete('/v1/account')
$response->noContent();
});
App::get('/v1/account/sessions')
->desc('List sessions')
->groups(['api', 'account'])
@@ -808,9 +719,8 @@ App::post('/v1/account/sessions/email')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->inject('hooks')
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks) {
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) {
$email = \strtolower($email);
$protocol = $request->getProtocol();
@@ -903,10 +813,6 @@ App::post('/v1/account/sessions/email')
->setParam('sessionId', $session->getId())
;
if ($project->getAttribute('auths', [])['sessionAlerts'] ?? false) {
sendSessionAlert($locale, $user, $project, $session, $queueForMails);
}
$response->dynamic($session, Response::MODEL_SESSION);
});
@@ -1075,7 +981,6 @@ App::post('/v1/account/sessions/token')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->action($createSession);
App::get('/v1/account/sessions/oauth2/:provider')
@@ -2237,7 +2142,6 @@ App::put('/v1/account/sessions/magic-url')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->action($createSession);
App::put('/v1/account/sessions/phone')
@@ -2268,7 +2172,6 @@ App::put('/v1/account/sessions/phone')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->action($createSession);
App::post('/v1/account/tokens/phone')
@@ -2371,18 +2274,7 @@ App::post('/v1/account/tokens/phone')
$dbForProject->purgeCachedDocument('users', $user->getId());
}
$secret = null;
$sendSMS = true;
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
foreach ($mockNumbers as $mockNumber) {
if ($mockNumber['phone'] === $phone) {
$secret = $mockNumber['otp'];
$sendSMS = false;
break;
}
}
$secret ??= Auth::codeGenerator();
$secret = Auth::codeGenerator();
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_OTP));
$token = new Document([
@@ -2407,37 +2299,35 @@ App::post('/v1/account/tokens/phone')
$dbForProject->purgeCachedDocument('users', $user->getId());
if ($sendSMS) {
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.login-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $token->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
$customTemplate = $project->getAttribute('templates', [])['sms.login-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $token->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $secret);
@@ -2452,8 +2342,7 @@ App::post('/v1/account/tokens/phone')
->dynamic($token, Response::MODEL_TOKEN);
});
App::post('/v1/account/jwts')
->alias('/v1/account/jwt')
App::post('/v1/account/jwt')
->desc('Create JWT')
->groups(['api', 'account', 'auth'])
->label('scope', 'account')
@@ -2486,11 +2375,15 @@ App::post('/v1/account/jwts')
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 0);
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => $jwt->encode([
// 'uid' => 1,
// 'aud' => 'http://site.com',
// 'scopes' => ['user'],
// 'iss' => 'http://api.mysite.com',
'userId' => $user->getId(),
'sessionId' => $current->getId(),
])]), Response::MODEL_JWT);
@@ -3452,8 +3345,7 @@ App::post('/v1/account/verification/phone')
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
$phone = $user->getAttribute('phone');
if (empty($phone)) {
if (empty($user->getAttribute('phone'))) {
throw new Exception(Exception::USER_PHONE_NOT_FOUND);
}
@@ -3464,19 +3356,7 @@ App::post('/v1/account/verification/phone')
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$secret = null;
$sendSMS = true;
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
foreach ($mockNumbers as $mockNumber) {
if ($mockNumber['phone'] === $phone) {
$secret = $mockNumber['otp'];
$sendSMS = false;
break;
}
}
$secret ??= Auth::codeGenerator();
$secret = Auth::codeGenerator();
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$verification = new Document([
@@ -3501,37 +3381,35 @@ App::post('/v1/account/verification/phone')
$dbForProject->purgeCachedDocument('users', $user->getId());
if ($sendSMS) {
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.verification-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $verification->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$user->getAttribute('phone')])
->setProviderType(MESSAGE_TYPE_SMS);
$customTemplate = $project->getAttribute('templates', [])['sms.verification-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $verification->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$user->getAttribute('phone')])
->setProviderType(MESSAGE_TYPE_SMS);
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $secret);
@@ -3682,22 +3560,12 @@ App::get('/v1/account/mfa/factors')
$recoveryCodeEnabled = \is_array($mfaRecoveryCodes) && \count($mfaRecoveryCodes) > 0;
$totp = TOTP::getAuthenticatorFromUser($user);
$webauthnAuths = WebAuthn::getAuthenticatorsFromUser($user) ?? [];
$webauthnVerified = false;
foreach ($webauthnAuths as $authenticator) {
if ($authenticator->getAttribute('verified', false)) {
$webauthnVerified = true;
break;
}
}
$factors = new Document([
Type::TOTP => $totp !== null && $totp->getAttribute('verified', false),
Type::EMAIL => $user->getAttribute('email', false) && $user->getAttribute('emailVerification', false),
Type::PHONE => $user->getAttribute('phone', false) && $user->getAttribute('phoneVerification', false),
Type::RECOVERY_CODE => $recoveryCodeEnabled,
Type::WEBAUTHN => $webauthnVerified,
Type::RECOVERY_CODE => $recoveryCodeEnabled
]);
$response->dynamic($factors, Response::MODEL_MFA_FACTORS);
@@ -3840,133 +3708,6 @@ App::put('/v1/account/mfa/authenticators/:type')
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
App::post('/v1/account/mfa/authenticators/webauthn')
->desc('Add WebAuthn Authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createWebauthnMfaAuthenticator')
->label('sdk.description', '/docs/references/account/create-webauthn-mfa-authenticator.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBAUTHN_REGISTER_CHALLENGE)
->label('sdk.offline.model', '/account')
->label('sdk.offline.key', 'current')
->inject('request')
->inject('response')
->inject('project')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Event $queueForEvents) {
// Clean up any previous challenges not completed
$authenticators = array_filter($user->getAttribute('authenticators', []), fn ($authenticator) => $authenticator['type'] === Type::WEBAUTHN);
foreach ($authenticators as $authenticator) {
/** @var Document $authenticator */
if (empty($authenticator->getAttribute('verified', false))) {
$dbForProject->deleteDocument('authenticators', $authenticator->getId());
}
}
$webauthn = new WebAuthn();
$relyingParty = $webauthn->createRelyingParty($project, $request);
$userEntity = $webauthn->createUserEntity($user);
$challenge = $webauthn->createRegisterChallenge($relyingParty, $userEntity, 60 * 5);
$authenticator = new Document([
'$id' => ID::unique(),
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'type' => Type::WEBAUTHN,
'verified' => false,
'data' => json_encode($challenge),
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
]
]);
$authenticator = $dbForProject->createDocument('authenticators', $authenticator);
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents->setParam('userId', $user->getId());
$model = new Document($challenge);
$response->dynamic($model, Response::MODEL_WEBAUTHN_REGISTER_CHALLENGE);
});
App::put('/v1/account/mfa/authenticators/webauthn')
->desc('Verify WebAuthn Authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateWebauthnMfaAuthenticator')
->label('sdk.description', '/docs/references/account/update-webauthn-mfa-authenticator.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->label('sdk.offline.model', '/account')
->label('sdk.offline.key', 'current')
->param('challengeResponse', '', new Text(8192), 'Valid verification token.')
->inject('response')
->inject('user')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $challengeResponse, Response $response, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
/** @var ?Document $authenticator */
$authenticator = null;
foreach ($user->getAttribute('authenticators', []) as $auth) {
if ($auth['type'] === Type::WEBAUTHN && empty($auth['verified'])) {
$authenticator = $auth;
}
};
if ($authenticator === null) {
throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND);
}
$webauthn = new WebAuthn();
$challenge = $authenticator->getAttribute('data');
$publicKeyCredentials = null;
try {
$publicKeyCredentials = $webauthn->verifyRegisterChallenge($challenge, $challengeResponse);
} catch (\Exception $e) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$authenticator->setAttribute('verified', true);
$authenticator->setAttribute('data', json_encode($publicKeyCredentials));
$dbForProject->updateDocument('authenticators', $authenticator->getId(), $authenticator);
$dbForProject->purgeCachedDocument('authenticators', $authenticator->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$factors = $session->getAttribute('factors', []);
$factors[] = Type::WEBAUTHN;
$factors = \array_unique($factors);
$session->setAttribute('factors', $factors);
$dbForProject->updateDocument('sessions', $session->getId(), $session);
$queueForEvents->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
App::post('/v1/account/mfa/recovery-codes')
->desc('Create MFA Recovery Codes')
->groups(['api', 'account'])
@@ -4082,7 +3823,7 @@ App::get('/v1/account/mfa/recovery-codes')
App::delete('/v1/account/mfa/authenticators/:type')
->desc('Delete Authenticator')
->groups(['api', 'account', 'mfaProtected'])
->groups(['api', 'account'])
->label('event', 'users.[userId].delete.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
@@ -4094,32 +3835,47 @@ App::delete('/v1/account/mfa/authenticators/:type')
->label('sdk.description', '/docs/references/account/delete-mfa-authenticator.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('type', null, new WhiteList([Type::TOTP, Type::WEBAUTHN]), 'Type of authenticator.')
->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.')
->param('otp', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $type, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$authenticators = [];
->action(function (string $type, string $otp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
switch ($type) {
case Type::TOTP:
$authenticators[] = TOTP::getAuthenticatorFromUser($user);
break;
case Type::WEBAUTHN:
$authenticators = WebAuthn::getAuthenticatorsFromUser($user);
break;
}
$authenticator = (match ($type) {
Type::TOTP => TOTP::getAuthenticatorFromUser($user),
default => null
});
if (empty($authenticators)) {
if (!$authenticator) {
throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND);
}
foreach ($authenticators as $authenticator) {
$dbForProject->deleteDocument('authenticators', $authenticator->getId());
$success = (match ($type) {
Type::TOTP => Challenge\TOTP::verify($user, $otp),
default => false
});
if (!$success) {
$mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []);
if (in_array($otp, $mfaRecoveryCodes)) {
$mfaRecoveryCodes = array_diff($mfaRecoveryCodes, [$otp]);
$mfaRecoveryCodes = array_values($mfaRecoveryCodes);
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
$dbForProject->updateDocument('users', $user->getId(), $user);
$success = true;
}
}
if (!$success) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$dbForProject->deleteDocument('authenticators', $authenticator->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
$queueForEvents->setParam('userId', $user->getId());
$response->noContent();
@@ -4400,173 +4156,6 @@ App::put('/v1/account/mfa/challenge')
$response->dynamic($session, Response::MODEL_SESSION);
});
App::post('/v1/account/mfa/challenge/webauthn')
->desc('Create WebAuthn MFA Challenge')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].challenges.[challengeId].create')
->label('audits.event', 'challenge.create')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
->label('sdk.method', 'createWebauthnMfaChallenge')
->label('sdk.description', '/docs/references/account/create-webauthn-mfa-challenge.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_WEBAUTHN_LOGIN_CHALLENGE)
->label('abuse-limit', 10)
->label('abuse-key', 'url:{url},token:{param-token}')
->inject('response')
->inject('dbForProject')
->inject('user')
->inject('locale')
->inject('project')
->inject('request')
->inject('queueForEvents')
->action(function (Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents) {
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_WEBAUTHN);
$webauthn = new WebAuthn();
$allowedCredentials = $webauthn->getAllowedCredentials($user);
if (empty($allowedCredentials)) {
throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND);
}
$relyingParty = $webauthn->createRelyingParty($project, $request);
$webAuthnChallenge = $webauthn->createLoginChallenge($relyingParty, $allowedCredentials, Auth::TOKEN_EXPIRATION_WEBAUTHN);
// Store challenge
$challenge = new Document([
'userId' => $user->getId(),
'userInternalId' => $user->getInternalId(),
'type' => Type::WEBAUTHN,
'code' => $webAuthnChallenge['challenge'],
'expire' => $expire,
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
]);
$challenge = $dbForProject->createDocument('challenges', $challenge);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('challengeId', $challenge->getId());
// Send challenge
$response->dynamic(new Document(array_merge(
[
'$id' => $challenge->getId(),
],
$webAuthnChallenge
)), Response::MODEL_WEBAUTHN_LOGIN_CHALLENGE);
});
App::put('/v1/account/mfa/challenge/webauthn')
->desc('Create WebAuthn MFA Challenge (confirmation)')
->groups(['api', 'account', 'mfa'])
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].create')
->label('audits.event', 'challenges.update')
->label('audits.resource', 'user/{response.userId}')
->label('audits.userId', '{response.userId}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'updateWebauthnMfaChallenge')
->label('sdk.description', '/docs/references/account/update-webauthn-mfa-challenge.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_SESSION)
->label('abuse-limit', 10)
->label('abuse-key', 'userId:{param-userId}')
->param('challengeId', '', new Text(256), 'ID of the challenge.')
->param('challengeResponse', '', new Text(8192), 'Valid verification token.')
->inject('project')
->inject('response')
->inject('request')
->inject('user')
->inject('session')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $challengeId, string $challengeResponse, Document $project, Response $response, Request $request, Document $user, Document $session, Database $dbForProject, Event $queueForEvents) {
$challenge = $dbForProject->getDocument('challenges', $challengeId);
if ($challenge->isEmpty()) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$authenticators = array_filter(Webauthn::getAuthenticatorsFromUser($user), function ($auth) {
return !empty($auth['verified']);
});
$webauthn = new WebAuthn();
$relyingParty = $webauthn->createRelyingParty($project, $request);
$responseJson = json_decode($challengeResponse, true);
// Find authenticator used
$authenticator = null;
foreach ($authenticators as $auth) {
$data = $auth['data'];
if ($data['publicKeyCredentialId'] == $responseJson['id']) {
$authenticator = $auth;
break;
}
}
if ($authenticator === null) {
throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND);
}
/** @var Document $authenticator */
// Check challenge
$publicKeyCredential = null;
try {
$publicKeyCredential = $webauthn->verifyLoginChallenge(
challenge: $challenge->getArrayCopy(),
challengeResponse: $challengeResponse,
hostname: $request->gethostname(),
timeout: Auth::TOKEN_EXPIRATION_WEBAUTHN,
allowCredentials: $webauthn->getAllowedCredentials($user),
rpEntity: $relyingParty,
authenticatorPublicKey: $webauthn->deserializePublicKeyCredentialSource($authenticator->getAttribute('data', []))
);
} catch (\Exception $e) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
// Update authenticator as counter has changed
$dbForProject->updateDocument('authenticators', $authenticator->getId(), new Document([
'data' => json_encode($publicKeyCredential)
]));
// Update Session
Authorization::skip(function () use ($dbForProject, $challengeId) {
$dbForProject->deleteDocument('challenges', $challengeId);
});
$dbForProject->purgeCachedDocument('users', $user->getId());
$factors = $session->getAttribute('factors', []);
$factors[] = TYPE::WEBAUTHN;
$factors = \array_unique($factors);
$session
->setAttribute('factors', $factors)
->setAttribute('mfaUpdatedAt', DateTime::now());
$dbForProject->updateDocument('sessions', $session->getId(), $session);
$queueForEvents
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId());
$response->dynamic($session, Response::MODEL_SESSION);
});
App::post('/v1/account/targets/push')
->desc('Create push target')
->groups(['api', 'account'])
+44 -234
View File
@@ -11,7 +11,6 @@ use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Platform\Tasks\ScheduleExecutions;
use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
@@ -34,7 +33,6 @@ use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Roles;
use Utopia\Database\Validator\UID;
use Utopia\Storage\Device;
@@ -153,7 +151,6 @@ App::post('/v1/functions')
->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Control System) deployment.', true)
->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function.', true)
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true)
@@ -172,7 +169,7 @@ App::post('/v1/functions')
->inject('queueForBuilds')
->inject('dbForConsole')
->inject('gitHub')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
@@ -222,9 +219,8 @@ App::post('/v1/functions')
'timeout' => $timeout,
'entrypoint' => $entrypoint,
'commands' => $commands,
'scopes' => $scopes,
'search' => implode(' ', [$functionId, $name, $runtime]),
'version' => 'v4',
'version' => 'v3',
'installationId' => $installation->getId(),
'installationInternalId' => $installation->getInternalId(),
'providerRepositoryId' => $providerRepositoryId,
@@ -686,7 +682,6 @@ App::put('/v1/functions/:functionId')
->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API Key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Controle System) deployment.', true)
->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true)
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true)
@@ -700,7 +695,7 @@ App::put('/v1/functions/:functionId')
->inject('queueForBuilds')
->inject('dbForConsole')
->inject('gitHub')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
// TODO: If only branch changes, re-deploy
$function = $dbForProject->getDocument('functions', $functionId);
@@ -812,7 +807,6 @@ App::put('/v1/functions/:functionId')
'logging' => $logging,
'entrypoint' => $entrypoint,
'commands' => $commands,
'scopes' => $scopes,
'installationId' => $installation->getId(),
'installationInternalId' => $installation->getInternalId(),
'providerRepositoryId' => $providerRepositoryId,
@@ -1166,7 +1160,6 @@ App::post('/v1/functions/:functionId/deployments')
}
$activate = (bool) filter_var($activate, FILTER_VALIDATE_BOOLEAN);
$type = $request->getHeader('x-sdk-language') === 'cli' ? 'cli' : 'manual';
if ($chunksUploaded === $chunks) {
if ($activate) {
@@ -1204,7 +1197,7 @@ App::post('/v1/functions/:functionId/deployments')
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => $activate,
'metadata' => $metadata,
'type' => $type
'type' => 'manual'
]));
} else {
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('size', $fileSize)->setAttribute('metadata', $metadata));
@@ -1237,7 +1230,7 @@ App::post('/v1/functions/:functionId/deployments')
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => $activate,
'metadata' => $metadata,
'type' => $type
'type' => 'manual'
]));
} else {
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('chunksUploaded', $chunksUploaded)->setAttribute('metadata', $metadata));
@@ -1437,10 +1430,9 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
$response->noContent();
});
App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->groups(['api', 'functions'])
->desc('Rebuild deployment')
->desc('Create build')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
->label('audits.event', 'deployment.update')
@@ -1448,11 +1440,12 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createBuild')
->label('sdk.description', '/docs/references/functions/create-build.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.')
->param('deploymentId', '', new UID(), 'Deployment ID.')
->param('buildId', '', new UID(), 'Build unique ID.', true) // added as optional param for backward compatibility
->param('buildId', '', new UID(), 'Build unique ID.')
->inject('request')
->inject('response')
->inject('dbForProject')
@@ -1460,17 +1453,25 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->inject('queueForEvents')
->inject('queueForBuilds')
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId));
if ($build->isEmpty()) {
throw new Exception(Exception::BUILD_NOT_FOUND);
}
$deploymentId = ID::unique();
$deployment->removeAttribute('$internalId');
@@ -1495,86 +1496,6 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
$response->noContent();
});
App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->groups(['api', 'functions'])
->desc('Cancel deployment')
->label('scope', 'functions.write')
->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateDeploymentBuild')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUILD)
->param('functionId', '', new UID(), 'Function ID.')
->param('deploymentId', '', new UID(), 'Deployment ID.')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
$buildId = ID::unique();
$build = $dbForProject->createDocument('builds', new Document([
'$id' => $buildId,
'$permissions' => [],
'startTime' => DateTime::now(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentId' => $deployment->getId(),
'status' => 'canceled',
'path' => '',
'runtime' => $function->getAttribute('runtime'),
'source' => $deployment->getAttribute('path', ''),
'sourceType' => '',
'logs' => '',
'duration' => 0,
'size' => 0
]));
$deployment->setAttribute('buildId', $build->getId());
$deployment->setAttribute('buildInternalId', $build->getInternalId());
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
} else {
if (\in_array($build->getAttribute('status'), ['ready', 'failed'])) {
throw new Exception(Exception::BUILD_ALREADY_COMPLETED);
}
$startTime = new \DateTime($build->getAttribute('startTime'));
$endTime = new \DateTime('now');
$duration = $endTime->getTimestamp() - $startTime->getTimestamp();
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttributes([
'endTime' => DateTime::now(),
'duration' => $duration,
'status' => 'canceled'
]));
}
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
$deleteBuild = $executor->deleteRuntime($project->getId(), $deploymentId . "-build");
$queueForEvents
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response->dynamic($build, Response::MODEL_BUILD);
});
App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
->desc('Create execution')
@@ -1593,21 +1514,16 @@ App::post('/v1/functions/:functionId/executions')
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
->param('headers', [], new Assoc(), 'HTTP headers of execution. Defaults to empty.', true)
->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true)
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('dbForConsole')
->inject('user')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->inject('queueForFunctions')
->inject('geodb')
->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, ?string $scheduledAt, Response $response, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
if(!$async && !is_null($scheduledAt)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.');
}
->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb) {
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
@@ -1666,8 +1582,7 @@ App::post('/v1/functions/:functionId/executions')
}
if (!$current->isEmpty()) {
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
'sessionId' => $current->getId(),
@@ -1675,14 +1590,6 @@ App::post('/v1/functions/:functionId/executions')
}
}
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$apiKey = $jwtObj->encode([
'projectId' => $project->getId(),
'scopes' => $function->getAttribute('scopes', [])
]);
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
$headers['x-appwrite-trigger'] = 'http';
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
@@ -1712,12 +1619,6 @@ App::post('/v1/functions/:functionId/executions')
$executionId = ID::unique();
$status = $async ? 'waiting' : 'processing';
if(!is_null($scheduledAt)) {
$status = 'scheduled';
}
$execution = new Document([
'$id' => $executionId,
'$permissions' => !$user->isEmpty() ? [Permission::read(Role::user($user->getId()))] : [],
@@ -1725,8 +1626,8 @@ App::post('/v1/functions/:functionId/executions')
'functionId' => $function->getId(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentId' => $deployment->getId(),
'trigger' => (!is_null($scheduledAt)) ? 'schedule' : 'http',
'status' => $status, // waiting / processing / completed / failed / scheduled
'trigger' => 'http', // http / schedule / event
'status' => $async ? 'waiting' : 'processing', // waiting / processing / completed / failed
'responseStatusCode' => 0,
'responseHeaders' => [],
'requestPath' => $path,
@@ -1744,45 +1645,26 @@ App::post('/v1/functions/:functionId/executions')
->setContext('function', $function);
if ($async) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
if(is_null($scheduledAt)) {
$queueForFunctions
->setType('http')
->setExecution($execution)
->setFunction($function)
->setBody($body)
->setHeaders($headers)
->setPath($path)
->setMethod($method)
->setJWT($jwt)
->setProject($project)
->setUser($user)
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->trigger();
} else {
$data = [
'headers' => $headers,
'path' => $path,
'method' => $method,
'body' => $body,
'jwt' => $jwt,
];
$dbForConsole->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => ScheduleExecutions::getSupportedResource(),
'resourceId' => $execution->getId(),
'resourceInternalId' => $execution->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'data' => $data,
'active' => true,
]));
if ($function->getAttribute('logging')) {
/** @var Document $execution */
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$queueForFunctions
->setType('http')
->setExecution($execution)
->setFunction($function)
->setBody($body)
->setHeaders($headers)
->setPath($path)
->setMethod($method)
->setJWT($jwt)
->setProject($project)
->setUser($user)
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->trigger();
return $response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($execution, Response::MODEL_EXECUTION);
@@ -1812,20 +1694,14 @@ App::post('/v1/functions/:functionId/executions')
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$endpoint = $protocol . '://' . $hostname . "/v1";
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE
]);
/** Execute function */
@@ -1848,7 +1724,6 @@ App::post('/v1/functions/:functionId/executions')
method: $method,
headers: $headers,
runtimeEntrypoint: $command,
logging: $function->getAttribute('logging', true),
requestTimeout: 30
);
@@ -1888,7 +1763,10 @@ App::post('/v1/functions/:functionId/executions')
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE), (int)($execution->getAttribute('duration') * 1000)) // per function
;
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
if ($function->getAttribute('logging')) {
/** @var Document $execution */
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
}
$roles = Authorization::getRoles();
@@ -2041,74 +1919,6 @@ App::get('/v1/functions/:functionId/executions/:executionId')
$response->dynamic($execution, Response::MODEL_EXECUTION);
});
App::delete('/v1/functions/:functionId/executions/:executionId')
->groups(['api', 'functions'])
->desc('Delete execution')
->label('scope', 'execution.write')
->label('event', 'functions.[functionId].executions.[executionId].delete')
->label('audits.event', 'executions.delete')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'deleteExecution')
->label('sdk.description', '/docs/references/functions/delete-execution.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.')
->param('executionId', '', new UID(), 'Execution ID.')
->inject('response')
->inject('dbForProject')
->inject('dbForConsole')
->inject('queueForEvents')
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$execution = $dbForProject->getDocument('executions', $executionId);
if ($execution->isEmpty()) {
throw new Exception(Exception::EXECUTION_NOT_FOUND);
}
if ($execution->getAttribute('functionId') !== $function->getId()) {
throw new Exception(Exception::EXECUTION_NOT_FOUND);
}
$status = $execution->getAttribute('status');
if (!in_array($status, ['completed', 'failed', 'scheduled'])) {
throw new Exception(Exception::EXECUTION_IN_PROGRESS);
}
if (!$dbForProject->deleteDocument('executions', $execution->getId())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove execution from DB');
}
if ($status === 'scheduled') {
$schedule = $dbForConsole->findOne('schedules', [
Query::equal('resourceId', [$execution->getId()]),
Query::equal('resourceType', [ScheduleExecutions::getSupportedResource()]),
Query::equal('active', [true]),
]);
if ($schedule && !$schedule->isEmpty()) {
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('active', false);
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
}
}
$queueForEvents
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->setPayload($response->output($execution, Response::MODEL_EXECUTION));
$response->noContent();
});
// Variables
App::post('/v1/functions/:functionId/variables')
+9 -5
View File
@@ -2697,7 +2697,7 @@ App::post('/v1/messaging/messages/email')
'resourceInternalId' => $message->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'schedule' => $scheduledAt,
'active' => true,
]));
@@ -2813,7 +2813,7 @@ App::post('/v1/messaging/messages/sms')
'resourceInternalId' => $message->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'schedule' => $scheduledAt,
'active' => true,
]));
@@ -2939,9 +2939,11 @@ App::post('/v1/messaging/messages/push')
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
}
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', \intval($expiry), 0);
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
$jwt = $encoder->encode([
'iat' => \time(),
'exp' => $expiry,
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'projectId' => $project->getId(),
@@ -2989,7 +2991,7 @@ App::post('/v1/messaging/messages/push')
'resourceInternalId' => $message->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'schedule' => $scheduledAt,
'active' => true,
]));
@@ -3799,9 +3801,11 @@ App::patch('/v1/messaging/messages/push/:messageId')
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
}
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', \intval($expiry), 0);
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
$jwt = $encoder->encode([
'iat' => \time(),
'exp' => $expiry,
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'projectId' => $project->getId(),
+3 -107
View File
@@ -1,8 +1,6 @@
<?php
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\MockNumber;
use Appwrite\Event\Delete;
use Appwrite\Event\Mail;
use Appwrite\Event\Validator\Event;
@@ -22,7 +20,6 @@ use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Query as QueryException;
@@ -106,11 +103,8 @@ App::post('/v1/projects')
'passwordHistory' => 0,
'passwordDictionary' => false,
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
'personalDataCheck' => false,
'mockNumbers' => [],
'sessionAlerts' => false,
'personalDataCheck' => false
];
foreach ($auth as $method) {
$auths[$method['key'] ?? ''] = true;
}
@@ -173,7 +167,6 @@ App::post('/v1/projects')
'webhooks' => null,
'keys' => null,
'auths' => $auths,
'accessedAt' => DateTime::now(),
'search' => implode(' ', [$projectId, $name]),
'database' => $dsn,
]));
@@ -368,7 +361,7 @@ App::patch('/v1/projects/:projectId')
});
App::patch('/v1/projects/:projectId/team')
->desc('Update project team')
->desc('Update Project Team')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -609,37 +602,6 @@ App::patch('/v1/projects/:projectId/oauth2')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/session-alerts')
->desc('Update project sessions emails')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateSessionAlerts')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('alerts', false, new Boolean(true), 'Set to true to enable session emails.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $alerts, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['sessionAlerts'] = $alerts;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/limit')
->desc('Update project users limit')
->groups(['api', 'projects'])
@@ -860,37 +822,6 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/mock-numbers')
->desc('Update the mock numbers for the project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateMockNumbers')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('numbers', '', new ArrayList(new MockNumber(), 10), 'An array of mock numbers and their corresponding verification codes (OTPs). Each number should be a valid E.164 formatted phone number. Maximum of 10 numbers are allowed.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, array $numbers, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['mockNumbers'] = $numbers;
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::delete('/v1/projects/:projectId')
->desc('Delete project')
->groups(['api', 'projects'])
@@ -1224,7 +1155,7 @@ App::post('/v1/projects/:projectId/keys')
'expire' => $expire,
'sdks' => [],
'accessedAt' => null,
'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)),
'secret' => \bin2hex(\random_bytes(128)),
]);
$key = $dbForConsole->createDocument('keys', $key);
@@ -1385,41 +1316,6 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
$response->noContent();
});
// JWT Keys
App::post('/v1/projects/:projectId/jwts')
->groups(['api', 'projects'])
->desc('Create JWT')
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createJWT')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_JWT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, array $scopes, int $duration, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => API_KEY_DYNAMIC . '_' . $jwt->encode([
'projectId' => $project->getId(),
'scopes' => $scopes
])]), Response::MODEL_JWT);
});
// Platforms
App::post('/v1/projects/:projectId/platforms')
+3 -2
View File
@@ -1309,7 +1309,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
try {
$decoded = $decoder->decode($jwt);
@@ -1320,7 +1320,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
if (
$decoded['projectId'] !== $project->getId() ||
$decoded['bucketId'] !== $bucketId ||
$decoded['fileId'] !== $fileId
$decoded['fileId'] !== $fileId ||
$decoded['exp'] < \time()
) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
-56
View File
@@ -1,6 +1,5 @@
<?php
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Auth\MFA\Type;
use Appwrite\Auth\MFA\Type\TOTP;
@@ -40,7 +39,6 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
@@ -2097,60 +2095,6 @@ App::delete('/v1/users/identities/:identityId')
return $response->noContent();
});
App::post('/v1/users/:userId/jwts')
->desc('Create user JWT')
->groups(['api', 'users'])
->label('scope', 'users.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users')
->label('sdk.method', 'createJWT')
->label('sdk.description', '/docs/references/users/create-user-jwt.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_JWT)
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', 'recent', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true)
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $sessionId, int $duration, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$sessions = $user->getAttribute('sessions', []);
$session = new Document();
if($sessionId === 'recent') {
// Get most recent
$session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document();
} else {
// Find by ID
foreach ($sessions as $loopSession) { /** @var Utopia\Database\Document $loopSession */
if ($loopSession->getId() == $sessionId) {
$session = $loopSession;
break;
}
}
}
if ($session->isEmpty()) {
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => $jwt->encode([
'userId' => $user->getId(),
'sessionId' => $session->getId()
])]), Response::MODEL_JWT);
});
App::get('/v1/users/usage')
->desc('Get users usage stats')
->groups(['api', 'users'])
-63
View File
@@ -464,67 +464,6 @@ App::get('/v1/vcs/github/callback')
->redirect($redirectSuccess);
});
App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/contents')
->desc('Get files and directories of a VCS repository')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('sdk.namespace', 'vcs')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.method', 'getRepositoryContents')
->label('sdk.description', '')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VCS_CONTENT_LIST)
->param('installationId', '', new Text(256), 'Installation Id')
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
->param('providerRootDirectory', '', new Text(256, 0), 'Path to get contents of nested directory', true)
->inject('gitHub')
->inject('response')
->inject('project')
->inject('dbForConsole')
->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, GitHub $github, Response $response, Document $project, Database $dbForConsole) {
$installation = $dbForConsole->getDocument('installations', $installationId);
if ($installation->isEmpty()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId);
try {
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
if (empty($repositoryName)) {
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
}
} catch (RepositoryNotFound $e) {
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
}
$contents = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory);
$vcsContents = [];
foreach ($contents as $content) {
$isDirectory = false;
if($content['type'] === GitHub::CONTENTS_DIRECTORY) {
$isDirectory = true;
}
$vcsContents[] = new Document([
'isDirectory' => $isDirectory,
'name' => $content['name'] ?? '',
'size' => $content['size'] ?? 0
]);
}
$response->dynamic(new Document([
'contents' => $vcsContents
]), Response::MODEL_VCS_CONTENT_LIST);
});
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
->desc('Detect runtime settings from source code')
->groups(['api', 'vcs'])
@@ -566,7 +505,6 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
}
$files = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory);
$files = \array_column($files, 'name');
$languages = $github->listRepositoryLanguages($owner, $repositoryName);
$detectorFactory = new Detector($files, $languages);
@@ -648,7 +586,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
return function () use ($repo, $github) {
try {
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
$files = \array_column($files, 'name');
$languages = $github->listRepositoryLanguages($repo['organization'], $repo['name']);
$detectorFactory = new Detector($files, $languages);
+9 -4
View File
@@ -250,7 +250,6 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE
]);
/** Execute function */
@@ -273,7 +272,6 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
method: $method,
headers: $headers,
runtimeEntrypoint: $command,
logging: $function->getAttribute('logging', true),
requestTimeout: 30
);
@@ -333,6 +331,13 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$body = $execution['responseBody'] ?? '';
$encodingKey = \array_search('x-open-runtimes-encoding', \array_column($execution['responseHeaders'], 'name'));
if ($encodingKey !== false) {
if (($execution['responseHeaders'][$encodingKey]['value'] ?? '') === 'base64') {
$body = \base64_decode($body);
}
}
$contentType = 'text/plain';
foreach ($execution['responseHeaders'] as $header) {
if (\strtolower($header['name']) === 'content-type') {
@@ -896,7 +901,7 @@ App::get('/robots.txt')
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($host === $mainDomain || $host === 'localhost') {
if ($host === $mainDomain) {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
@@ -921,7 +926,7 @@ App::get('/humans.txt')
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($host === $mainDomain || $host === 'localhost') {
if ($host === $mainDomain) {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
-50
View File
@@ -6,7 +6,6 @@ use Appwrite\Extend\Exception;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@@ -155,55 +154,6 @@ App::patch('/v1/mock/functions-v2')
$response->noContent();
});
App::post('/v1/mock/api-key-unprefixed')
->desc('Create API Key (without standard prefix)')
->groups(['mock', 'api', 'projects'])
->label('scope', 'projects.write')
->label('docs', false)
->param('projectId', '', new UID(), 'Project ID.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
if (!$isDevelopment) {
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
}
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$scopes = array_keys(Config::getParam('scopes'));
$key = new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'projectInternalId' => $project->getInternalId(),
'projectId' => $project->getId(),
'name' => 'Outdated key',
'scopes' => $scopes,
'expire' => null,
'sdks' => [],
'accessedAt' => null,
'secret' => \bin2hex(\random_bytes(128)),
]);
$key = $dbForConsole->createDocument('keys', $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($key, Response::MODEL_KEY);
});
App::get('/v1/mock/github/callback')
->desc('Create installation document using GitHub installation id')
->groups(['mock', 'api', 'vcs'])
+35 -101
View File
@@ -1,10 +1,7 @@
<?php
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Auth\MFA\Type\TOTP;
use Appwrite\Auth\MFA\Type\WebAuthn;
use Appwrite\Event\Audit;
use Appwrite\Event\Build;
use Appwrite\Event\Database as EventDatabase;
@@ -198,99 +195,56 @@ App::init()
$scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route
$scopes = $roles[$role]['scopes']; // Allowed scopes for user role
$apiKey = $request->getHeader('x-appwrite-key', '');
$authKey = $request->getHeader('x-appwrite-key', '');
// API Key authentication
if (!empty($apiKey)) {
if (!empty($authKey)) { // API Key authentication
// Do not allow API key and session to be set at the same time
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
}
if(!\str_contains($apiKey, '_')) {
$keyType = API_KEY_STANDARD;
$authKey = $apiKey;
} else {
[ $keyType, $authKey ] = \explode('_', $apiKey, 2);
}
// Check if given key match project API keys
$key = $project->find('secret', $authKey, 'keys');
if ($key) {
$user = new Document([
'$id' => '',
'status' => true,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
if($keyType === API_KEY_DYNAMIC) {
// Dynamic key
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$payload = $jwtObj->decode($authKey);
} catch (JWTException $error) {
throw new Exception(Exception::API_KEY_EXPIRED);
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
}
$projectId = $payload['projectId'] ?? '';
$tokenScopes = $payload['scopes'] ?? [];
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
// JWT includes project ID for better security
if ($projectId === $project->getId()) {
$user = new Document([
'$id' => '',
'status' => true,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $tokenScopes);
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
$accessedAt = $key->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DateTime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
} elseif($keyType === API_KEY_STANDARD) {
// No underline means no prefix. Backwards compatibility.
// Regular key
// Check if given key match project API keys
$key = $project->find('secret', $apiKey, 'keys');
if ($key) {
$user = new Document([
'$id' => '',
'status' => true,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
}
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
$accessedAt = $key->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DateTime::now());
/** Update access time as well */
$key->setAttribute('accessedAt', Datetime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
/** Update access time as well */
$key->setAttribute('accessedAt', Datetime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
}
}
}
}
@@ -331,16 +285,7 @@ App::init()
$hasVerifiedEmail = $user->getAttribute('emailVerification', false);
$hasVerifiedPhone = $user->getAttribute('phoneVerification', false);
$hasVerifiedAuthenticator = TOTP::getAuthenticatorFromUser($user)?->getAttribute('verified') ?? false;
$webauthnAuthenticators = WebAuthn::getAuthenticatorsFromUser($user) ?? [];
$hasVerifiedWebAuthn = false;
foreach ($webauthnAuthenticators as $authenticator) {
if ($authenticator->getAttribute('verified')) {
$hasVerifiedWebAuthn = true;
break;
}
}
$hasMoreFactors = $hasVerifiedEmail || $hasVerifiedPhone || $hasVerifiedAuthenticator || $hasVerifiedWebAuthn;
$hasMoreFactors = $hasVerifiedEmail || $hasVerifiedPhone || $hasVerifiedAuthenticator;
$minimumFactors = ($mfaEnabled && $hasMoreFactors) ? 2 : 1;
if (!in_array('mfa', $route->getGroups())) {
@@ -770,23 +715,12 @@ App::shutdown()
->trigger();
}
/**
* Update project last activity
*/
if (!$project->isEmpty() && $project->getId() !== 'console') {
$accessedAt = $project->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project));
}
}
/**
* Update user last activity
*/
if (!$user->isEmpty()) {
$accessedAt = $user->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) {
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCCESS)) > $accessedAt) {
$user->setAttribute('accessedAt', DateTime::now());
if (APP_MODE_ADMIN !== $mode) {
+16 -50
View File
@@ -62,10 +62,6 @@ use Utopia\Database\Validator\Structure;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
use Utopia\Locale\Locale;
use Utopia\Logger\Adapter\AppSignal;
use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Utopia\Pools\Group;
@@ -113,9 +109,8 @@ const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4314;
const APP_VERSION_STABLE = '1.5.7';
@@ -213,9 +208,6 @@ const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
const MESSAGE_TYPE_EMAIL = 'email';
const MESSAGE_TYPE_SMS = 'sms';
const MESSAGE_TYPE_PUSH = 'push';
// API key types
const API_KEY_STANDARD = 'standard';
const API_KEY_DYNAMIC = 'dynamic';
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
@@ -237,19 +229,11 @@ const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
const METRIC_BUILDS = 'builds';
const METRIC_BUILDS_SUCCESS = 'builds.success';
const METRIC_BUILDS_FAILED = 'builds.failed';
const METRIC_BUILDS_STORAGE = 'builds.storage';
const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_EXECUTIONS = 'executions';
@@ -357,7 +341,8 @@ Database::addFilter(
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
$attribute
->setAttribute('min', $formatOptions['min'])
->setAttribute('max', $formatOptions['max']);
->setAttribute('max', $formatOptions['max'])
;
}
return $value;
@@ -740,26 +725,6 @@ $register->set('logger', function () {
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
try {
$loggingProvider = new DSN($providerConfig ?? '');
$providerName = $loggingProvider->getScheme();
$providerConfig = match ($providerName) {
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
default => ['key' => $loggingProvider->getHost()],
};
} catch (Throwable) {
// Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables
$configChunks = \explode(";", $providerConfig);
$providerConfig = match ($providerName) {
'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',],
'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''],
default => ['key' => $providerConfig],
};
}
if (empty($providerName) || empty($providerConfig)) {
return;
}
@@ -768,17 +733,20 @@ $register->set('logger', function () {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
}
$adapter = match ($providerName) {
'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']),
'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']),
'raygun' => new Raygun($providerConfig['key']),
'appsignal' => new AppSignal($providerConfig['key']),
default => throw new Exception('Provider "' . $providerName . '" not supported.')
};
// Old Sentry Format conversion. Fallback until the old syntax is completely deprecated.
if (str_contains($providerConfig, ';') && strtolower($providerName) == 'sentry') {
$configChunks = \explode(";", $providerConfig);
$sentryKey = $configChunks[0];
$projectId = $configChunks[1];
$providerConfig = 'https://' . $sentryKey . '@sentry.io/' . $projectId;
}
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
$adapter = new $classname($providerConfig);
return new Logger($adapter);
});
$register->set('pools', function () {
$group = new Group();
@@ -1233,7 +1201,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
try {
$payload = $jwt->decode($authJWT);
@@ -1322,11 +1290,9 @@ App::setResource('console', function () {
'legalAddress' => '',
'legalTaxId' => '',
'auths' => [
'mockNumbers' => [],
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled'
],
'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
+49 -56
View File
@@ -1,17 +1,20 @@
x-logging: &x-logging
logging:
driver: 'json-file'
options:
max-file: '5'
max-size: '10m'
<?php
$httpPort = $this->getParam('httpPort', '');
$httpsPort = $this->getParam('httpsPort', '');
$version = $this->getParam('version', '');
$registry = $this->getParam('registry', '');
$organization = $this->getParam('organization', '');
$registry = $this->getParam('registry', '');
$image = $this->getParam('image', '');
?>services:
?>x-logging: &x-logging
logging:
driver: 'json-file'
options:
max-file: '5'
max-size: '10m'
services:
traefik:
image: traefik:2.11
container_name: appwrite-traefik
@@ -26,8 +29,8 @@ $image = $this->getParam('image', '');
- --entrypoints.appwrite_websecure.address=:443
restart: unless-stopped
ports:
- <?php echo $httpPort; ?>:80
- <?php echo $httpsPort; ?>:443
- <?= $httpPort; ?>:80
- <?= $httpsPort; ?>:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- appwrite-config:/storage/config:ro
@@ -39,7 +42,7 @@ $image = $this->getParam('image', '');
- appwrite
appwrite:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
container_name: appwrite
<<: *x-logging
restart: unless-stopped
@@ -75,7 +78,6 @@ $image = $this->getParam('image', '');
- _APP_LOCALE
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
- _APP_CONSOLE_WHITELIST_IPS
- _APP_CONSOLE_HOSTNAMES
- _APP_SYSTEM_EMAIL_NAME
@@ -139,6 +141,7 @@ $image = $this->getParam('image', '');
- _APP_FUNCTIONS_RUNTIMES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_DELAY
@@ -164,7 +167,7 @@ $image = $this->getParam('image', '');
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-realtime:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: realtime
container_name: appwrite-realtime
<<: *x-logging
@@ -204,10 +207,11 @@ $image = $this->getParam('image', '');
- _APP_DB_USER
- _APP_DB_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-audits:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-audits
<<: *x-logging
container_name: appwrite-worker-audits
@@ -230,10 +234,11 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-webhooks:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-webhooks
<<: *x-logging
container_name: appwrite-worker-webhooks
@@ -258,10 +263,11 @@ $image = $this->getParam('image', '');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-deletes:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-deletes
<<: *x-logging
container_name: appwrite-worker-deletes
@@ -311,12 +317,13 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
appwrite-worker-databases:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-databases
<<: *x-logging
container_name: appwrite-worker-databases
@@ -339,10 +346,11 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-builds:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-builds
<<: *x-logging
container_name: appwrite-worker-builds
@@ -370,6 +378,7 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_VCS_GITHUB_APP_NAME
- _APP_VCS_GITHUB_PRIVATE_KEY
@@ -405,7 +414,7 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_WASABI_BUCKET
appwrite-worker-certificates:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-certificates
<<: *x-logging
container_name: appwrite-worker-certificates
@@ -435,10 +444,11 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-functions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-functions
<<: *x-logging
container_name: appwrite-worker-functions
@@ -469,12 +479,14 @@ $image = $this->getParam('image', '');
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_USAGE_STATS
- _APP_DOCKER_HUB_USERNAME
- _APP_DOCKER_HUB_PASSWORD
- _APP_DOCKER_REGISTRY
- _APP_DOCKER_USERNAME
- _APP_DOCKER_PASSWORD
- _APP_LOGGING_CONFIG
- _APP_LOGGING_PROVIDER
appwrite-worker-mails:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-mails
<<: *x-logging
container_name: appwrite-worker-mails
@@ -503,10 +515,11 @@ $image = $this->getParam('image', '');
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-messaging:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-messaging
<<: *x-logging
container_name: appwrite-worker-messaging
@@ -530,6 +543,7 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_SMS_FROM
- _APP_SMS_PROVIDER
@@ -556,7 +570,7 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_WASABI_BUCKET
appwrite-worker-migrations:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-migrations
<<: *x-logging
container_name: appwrite-worker-migrations
@@ -581,12 +595,13 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
appwrite-task-maintenance:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: maintenance
<<: *x-logging
container_name: appwrite-task-maintenance
@@ -620,7 +635,7 @@ $image = $this->getParam('image', '');
- _APP_MAINTENANCE_RETENTION_SCHEDULES
appwrite-worker-usage:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-usage
container_name: appwrite-worker-usage
<<: *x-logging
@@ -644,11 +659,12 @@ $image = $this->getParam('image', '');
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-worker-usage-dump:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: worker-usage-dump
<<: *x-logging
container_name: appwrite-worker-usage-dump
@@ -671,11 +687,12 @@ $image = $this->getParam('image', '');
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-task-scheduler-functions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: schedule-functions
container_name: appwrite-task-scheduler-functions
<<: *x-logging
@@ -699,33 +716,8 @@ $image = $this->getParam('image', '');
- _APP_DB_USER
- _APP_DB_PASS
appwrite-task-scheduler-executions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: schedule-executions
container_name: appwrite-task-scheduler-executions
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
depends_on:
- mariadb
- redis
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-task-scheduler-messages:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
image: <?= $registry; ?>/<?= $organization; ?>/<?= $image; ?>:<?= $version."\n"; ?>
entrypoint: schedule-messages
container_name: appwrite-task-scheduler-messages
<<: *x-logging
@@ -780,11 +772,12 @@ $image = $this->getParam('image', '');
- OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD
- OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL
- OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD
- OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_USERNAME
- OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_PASSWORD
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
-3
View File
@@ -1,3 +0,0 @@
#!/bin/sh
php /usr/src/code/app/cli.php schedule-executions $@
+4 -5
View File
@@ -42,7 +42,7 @@
"ext-openssl": "*",
"ext-zlib": "*",
"ext-sockets": "*",
"appwrite/php-runtimes": "0.14.*",
"appwrite/php-runtimes": "0.13.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.38.*",
"utopia-php/analytics": "0.10.*",
@@ -69,7 +69,7 @@
"utopia-php/storage": "0.18.*",
"utopia-php/swoole": "0.8.*",
"utopia-php/system": "0.8.*",
"utopia-php/vcs": "0.7.*",
"utopia-php/vcs": "0.6.*",
"utopia-php/websocket": "0.1.*",
"matomo/device-detector": "6.1.*",
"dragonmantank/cron-expression": "3.3.2",
@@ -78,12 +78,11 @@
"adhocore/jwt": "1.1.2",
"spomky-labs/otphp": "^10.0",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.14.*",
"web-auth/webauthn-lib": "4.9.*"
"league/csv": "9.14.*"
},
"require-dev": {
"ext-fileinfo": "*",
"appwrite/sdk-generator": "0.39.*",
"appwrite/sdk-generator": "0.38.*",
"phpunit/phpunit": "9.5.20",
"swoole/ide-helper": "5.1.2",
"textalk/websocket": "1.5.7",
Generated
+83 -1053
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,6 +1,8 @@
zend_extension=xdebug
[xdebug]
xdebug.mode=develop,debug
xdebug.mode=develop,debug,profile
xdebug.client_host=host.docker.internal
xdebug.start_with_request=yes
xdebug.start_with_request=yes
xdebug.output_dir=/tmp/xdebug
xdebug.use_compression=false
+17 -33
View File
@@ -40,7 +40,6 @@ services:
networks:
- gateway
- appwrite
- runtimes
appwrite:
container_name: appwrite
@@ -98,7 +97,6 @@ services:
- _APP_LOCALE
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
- _APP_CONSOLE_WHITELIST_IPS
- _APP_CONSOLE_HOSTNAMES
- _APP_SYSTEM_EMAIL_NAME
@@ -162,6 +160,7 @@ services:
- _APP_FUNCTIONS_RUNTIMES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_RETENTION_EXECUTION
@@ -238,6 +237,7 @@ services:
- _APP_DB_USER
- _APP_DB_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
@@ -267,6 +267,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
@@ -298,6 +299,7 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_WEBHOOK_MAX_FAILED_ATTEMPTS
- _APP_DATABASE_SHARED_TABLES
@@ -354,6 +356,7 @@ services:
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
@@ -385,6 +388,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_WORKERS_NUM
- _APP_QUEUE_NAME
@@ -420,6 +424,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_VCS_GITHUB_APP_NAME
- _APP_VCS_GITHUB_PRIVATE_KEY
@@ -487,6 +492,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
@@ -508,8 +514,6 @@ services:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -561,6 +565,7 @@ services:
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
@@ -593,6 +598,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_SMS_FROM
- _APP_SMS_PROVIDER
@@ -650,6 +656,7 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
@@ -720,6 +727,7 @@ services:
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
@@ -751,6 +759,7 @@ services:
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
@@ -783,33 +792,6 @@ services:
- _APP_DB_PASS
- _APP_DATABASE_SHARED_TABLES
appwrite-task-scheduler-executions:
entrypoint: schedule-executions
<<: *x-logging
container_name: appwrite-task-scheduler-executions
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- redis
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-task-scheduler-messages:
entrypoint: schedule-messages
<<: *x-logging
@@ -851,7 +833,7 @@ services:
hostname: exc1
<<: *x-logging
stop_signal: SIGINT
image: openruntimes/executor:0.6.1
image: openruntimes/executor:0.5.5
restart: unless-stopped
networks:
- appwrite
@@ -872,7 +854,8 @@ services:
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v4
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v3
- OPR_EXECUTOR_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
@@ -910,6 +893,7 @@ services:
- OPR_PROXY_ENV=$_APP_ENV
- OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_PROXY_SECRET=$_APP_EXECUTOR_SECRET
- OPR_PROXY_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER
- OPR_PROXY_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_PROXY_ALGORITHM=random
- OPR_PROXY_EXECUTORS=exc1
@@ -8,4 +8,4 @@ X-Appwrite-JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...
{
"otp": "<OTP>"
}
}
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createAnonymousSession(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createEmailPasswordSession(
"email@example.com", // email
"password", // password
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,24 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createEmailToken(
"<USER_ID>", // userId
"email@example.com", // email
false, // phrase (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createJWT(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,25 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createMagicURLToken(
"<USER_ID>", // userId
"email@example.com", // email
"https://example.com", // url (optional)
false, // phrase (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
import io.appwrite.enums.AuthenticatorType;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createMfaAuthenticator(
AuthenticatorType.TOTP, // type
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
import io.appwrite.enums.AuthenticationFactor;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createMfaChallenge(
AuthenticationFactor.EMAIL, // factor
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createMfaRecoveryCodes(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,26 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
import io.appwrite.enums.OAuthProvider;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createOAuth2Session(
OAuthProvider.AMAZON, // provider
"https://example.com", // success (optional)
"https://example.com", // failure (optional)
listOf(), // scopes (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,26 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
import io.appwrite.enums.OAuthProvider;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createOAuth2Token(
OAuthProvider.AMAZON, // provider
"https://example.com", // success (optional)
"https://example.com", // failure (optional)
listOf(), // scopes (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createPhoneToken(
"<USER_ID>", // userId
"+12065550100", // phone
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createPhoneVerification(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,24 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createPushTarget(
"<TARGET_ID>", // targetId
"<IDENTIFIER>", // identifier
"<PROVIDER_ID>", // providerId (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createRecovery(
"email@example.com", // email
"https://example.com", // url
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createSession(
"<USER_ID>", // userId
"<SECRET>", // secret
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.createVerification(
"https://example.com", // url
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,25 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.create(
"<USER_ID>", // userId
"email@example.com", // email
"", // password
"<NAME>", // name (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.deleteIdentity(
"<IDENTITY_ID>", // identityId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,24 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
import io.appwrite.enums.AuthenticatorType;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.deleteMfaAuthenticator(
AuthenticatorType.TOTP, // type
"<OTP>", // otp
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.deletePushTarget(
"<TARGET_ID>", // targetId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.deleteSession(
"<SESSION_ID>", // sessionId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.deleteSessions(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.getMfaRecoveryCodes(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.getPrefs(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.getSession(
"<SESSION_ID>", // sessionId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.get(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.listIdentities(
listOf(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.listLogs(
listOf(), // queries (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.listMfaFactors(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.listSessions(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateEmail(
"email@example.com", // email
"password", // password
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateMFA(
false, // mfa
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateMagicURLSession(
"<USER_ID>", // userId
"<SECRET>", // secret
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,24 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
import io.appwrite.enums.AuthenticatorType;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateMfaAuthenticator(
AuthenticatorType.TOTP, // type
"<OTP>", // otp
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateMfaChallenge(
"<CHALLENGE_ID>", // challengeId
"<OTP>", // otp
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateMfaRecoveryCodes(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateName(
"<NAME>", // name
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updatePassword(
"", // password
"password", // oldPassword (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updatePhoneSession(
"<USER_ID>", // userId
"<SECRET>", // secret
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updatePhoneVerification(
"<USER_ID>", // userId
"<SECRET>", // secret
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updatePhone(
"+12065550100", // phone
"password", // password
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updatePrefs(
mapOf( "a" to "b" ), // prefs
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updatePushTarget(
"<TARGET_ID>", // targetId
"<IDENTIFIER>", // identifier
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,24 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateRecovery(
"<USER_ID>", // userId
"<SECRET>", // secret
"", // password
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateSession(
"<SESSION_ID>", // sessionId
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,18 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateStatus(new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
}));
@@ -1,23 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Account;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Account account = new Account(client);
account.updateVerification(
"<USER_ID>", // userId
"<SECRET>", // secret
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,26 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
import io.appwrite.enums.Browser;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getBrowser(
Browser.AVANT_BROWSER, // code
0, // width (optional)
0, // height (optional)
0, // quality (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,26 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
import io.appwrite.enums.CreditCard;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getCreditCard(
CreditCard.AMERICAN_EXPRESS, // code
0, // width (optional)
0, // height (optional)
0, // quality (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,22 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getFavicon(
"https://example.com", // url
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,26 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
import io.appwrite.enums.Flag;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getFlag(
Flag.AFGHANISTAN, // code
0, // width (optional)
0, // height (optional)
0, // quality (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,24 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getImage(
"https://example.com", // url
0, // width (optional)
0, // height (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,25 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getInitials(
"<NAME>", // name (optional)
0, // width (optional)
0, // height (optional)
"", // background (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,25 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Avatars;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Avatars avatars = new Avatars(client);
avatars.getQR(
"<TEXT>", // text
1, // size (optional)
0, // margin (optional)
false, // download (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,26 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://cloud.appwrite.io/v1") // Your API Endpoint
.setProject("&lt;YOUR_PROJECT_ID&gt;"); // Your project ID
Databases databases = new Databases(client);
databases.createDocument(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
mapOf( "a" to "b" ), // data
listOf("read("any")"), // permissions (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);

Some files were not shown because too many files have changed in this diff Show More