Merge branch 'feat-storage-buckets' into feat-s3-integration
This commit is contained in:
commit
ec9d4599c4
|
@ -1,24 +1,24 @@
|
|||
{
|
||||
"settings.inspire": "\"هنر خردمند بودن، هنر دانستن چیزی است که باید از آن غافل شد.\"",
|
||||
"settings.inspire": "\"هنر خردمند بودن این است که بدانید چه چیزی را نادیده بگیرید.\"",
|
||||
"settings.locale": "fa",
|
||||
"settings.direction": "rtl",
|
||||
"emails.sender": "تیم %s",
|
||||
"emails.verification.subject": "تأیید حساب",
|
||||
"emails.verification.hello": "سلام {{name}}",
|
||||
"emails.verification.body": "برای تأیید حسابتان پیوند زیر را دنبال کنید.",
|
||||
"emails.verification.footer": "اگر شما درخواست تأیید حساب ندادهاید، میتوانید این ایمیل را نادیده بگیرید.",
|
||||
"emails.verification.body": "برای تأیید ایمیلتان پیوند زیر را دنبال کنید.",
|
||||
"emails.verification.footer": "اگر شما درخواست تأیید حساب ندادهاید، میتوانید این پیام را نادیده بگیرید.",
|
||||
"emails.verification.thanks": "سپاس فراوان",
|
||||
"emails.verification.signature": "تیم {{name}}",
|
||||
"emails.magicSession.subject": "ورود به حساب کاربری",
|
||||
"emails.magicSession.hello": "سلام،",
|
||||
"emails.magicSession.body": "برای ورود به حسابتان پیوند زیر را دنبال کنید.",
|
||||
"emails.magicSession.footer": "اگر شما درخواست ورود به حساب کاربری ندادهاید، میتوانید این ایمیل را نادیده بگیرید.",
|
||||
"emails.magicSession.footer": "اگر شما درخواست ورود به حساب کاربری با استفاده از این ایمیل را ندادهاید، میتوانید این پیام را نادیده بگیرید.",
|
||||
"emails.magicSession.thanks": "سپاس فراوان",
|
||||
"emails.magicSession.signature": "تیم {{name}}",
|
||||
"emails.recovery.subject": "بازیابی گذرواژه",
|
||||
"emails.recovery.hello": "سلام {{name}}",
|
||||
"emails.recovery.body": "برای بازیابی گذرواژهتان پیوند زیر را دنبال کنید.",
|
||||
"emails.recovery.footer": "اگر شما درخواست بازیابی گذرواژه ندادهاید، میتوانید این ایمیل را نادیده بگیرید.",
|
||||
"emails.recovery.footer": "اگر شما درخواست بازیابی گذرواژه ندادهاید، میتوانید این پیام را نادیده بگیرید.",
|
||||
"emails.recovery.thanks": "سپاس فراوان",
|
||||
"emails.recovery.signature": "تیم {{name}}",
|
||||
"emails.invitation.subject": "دعوت به تیم %s در %s",
|
||||
|
|
|
@ -28,205 +28,205 @@
|
|||
"emails.invitation.thanks": "Go raibh maith agat",
|
||||
"emails.invitation.signature": "{{project}} foireann",
|
||||
"locale.country.unknown": "Neamhaithnid",
|
||||
"countries.af": "Afghanistan",
|
||||
"countries.ao": "Angola",
|
||||
"countries.al": "Albania",
|
||||
"countries.ad": "Andorra",
|
||||
"countries.ae": "United Arab Emirates",
|
||||
"countries.ar": "Argentina",
|
||||
"countries.am": "Armenia",
|
||||
"countries.ag": "Antigua and Barbuda",
|
||||
"countries.au": "Australia",
|
||||
"countries.at": "Austria",
|
||||
"countries.az": "Azerbaijan",
|
||||
"countries.bi": "Burundi",
|
||||
"countries.be": "Belgium",
|
||||
"countries.bj": "Benin",
|
||||
"countries.bf": "Burkina Faso",
|
||||
"countries.bd": "Bangladesh",
|
||||
"countries.bg": "Bulgaria",
|
||||
"countries.bh": "Bahrain",
|
||||
"countries.bs": "Bahamas",
|
||||
"countries.ba": "Bosnia and Herzegovina",
|
||||
"countries.by": "Belarus",
|
||||
"countries.bz": "Belize",
|
||||
"countries.bo": "Bolivia",
|
||||
"countries.br": "Brazil",
|
||||
"countries.bb": "Barbados",
|
||||
"countries.bn": "Brunei",
|
||||
"countries.bt": "Bhutan",
|
||||
"countries.bw": "Botswana",
|
||||
"countries.cf": "Central African Republic",
|
||||
"countries.ca": "Canada",
|
||||
"countries.ch": "Switzerland",
|
||||
"countries.cl": "Chile",
|
||||
"countries.cn": "China",
|
||||
"countries.ci": "Ivory Coast",
|
||||
"countries.cm": "Cameroon",
|
||||
"countries.cd": "DR Congo",
|
||||
"countries.cg": "Republic of the Congo",
|
||||
"countries.co": "Colombia",
|
||||
"countries.km": "Comoros",
|
||||
"countries.af": "An Afganastáin",
|
||||
"countries.ao": "Angóla",
|
||||
"countries.al": "Albáin",
|
||||
"countries.ad": "Andóra",
|
||||
"countries.ae": "Aontas na nÉimíríochtaí Arabacha",
|
||||
"countries.ar": "Airgintín",
|
||||
"countries.am": "Airméin",
|
||||
"countries.ag": "Antigua agus Barbúda",
|
||||
"countries.au": "Astráil",
|
||||
"countries.at": "Ostair",
|
||||
"countries.az": "Asarbaiseáin",
|
||||
"countries.bi": "An Bhurúin",
|
||||
"countries.be": "An Bheilg",
|
||||
"countries.bj": "Beinin",
|
||||
"countries.bf": "Buircíne Fasó",
|
||||
"countries.bd": "An Bhanglaidéis",
|
||||
"countries.bg": "An Bhulgáir",
|
||||
"countries.bh": "Bairéin",
|
||||
"countries.bs": "Na Bahámaí",
|
||||
"countries.ba": "An Bhoisnia agus an Heirseagaivéin",
|
||||
"countries.by": "An Bhealarúis",
|
||||
"countries.bz": "An Bheilís",
|
||||
"countries.bo": "An Bholaiv",
|
||||
"countries.br": "An Bhrasaíl",
|
||||
"countries.bb": "Barbadós",
|
||||
"countries.bn": "Brúiné",
|
||||
"countries.bt": "An Bhútáin",
|
||||
"countries.bw": "An Bhotsuáin",
|
||||
"countries.cf": "Poblacht na hAfraice Láir",
|
||||
"countries.ca": "Ceanada",
|
||||
"countries.ch": "An Eilvéis",
|
||||
"countries.cl": "An tSile",
|
||||
"countries.cn": "An tSín",
|
||||
"countries.ci": "Poblacht Côte d’Ivoire",
|
||||
"countries.cm": "Camarún",
|
||||
"countries.cd": "Poblacht Dhaonlathach an Chongó",
|
||||
"countries.cg": "Poblacht an Chongó",
|
||||
"countries.co": "An Cholóim",
|
||||
"countries.km": "Oileáin Chomóra",
|
||||
"countries.cv": "Cape Verde",
|
||||
"countries.cr": "Costa Rica",
|
||||
"countries.cu": "Cuba",
|
||||
"countries.cy": "Cyprus",
|
||||
"countries.cz": "Czechia",
|
||||
"countries.de": "Germany",
|
||||
"countries.cr": "Cósta Ríce",
|
||||
"countries.cu": "Cúba",
|
||||
"countries.cy": "An Chipir",
|
||||
"countries.cz": "An tSeicia",
|
||||
"countries.de": "An Ghearmáin",
|
||||
"countries.dj": "Djibouti",
|
||||
"countries.dm": "Dominica",
|
||||
"countries.dk": "Denmark",
|
||||
"countries.do": "Dominican Republic",
|
||||
"countries.dz": "Algeria",
|
||||
"countries.ec": "Ecuador",
|
||||
"countries.eg": "Egypt",
|
||||
"countries.er": "Eritrea",
|
||||
"countries.es": "Spain",
|
||||
"countries.ee": "Estonia",
|
||||
"countries.et": "Ethiopia",
|
||||
"countries.fi": "Finland",
|
||||
"countries.fj": "Fiji",
|
||||
"countries.fr": "France",
|
||||
"countries.fm": "Micronesia",
|
||||
"countries.ga": "Gabon",
|
||||
"countries.gb": "United Kingdom",
|
||||
"countries.ge": "Georgia",
|
||||
"countries.gh": "Ghana",
|
||||
"countries.gn": "Guinea",
|
||||
"countries.gm": "Gambia",
|
||||
"countries.gw": "Guinea-Bissau",
|
||||
"countries.gq": "Equatorial Guinea",
|
||||
"countries.gr": "Greece",
|
||||
"countries.gd": "Grenada",
|
||||
"countries.gt": "Guatemala",
|
||||
"countries.gy": "Guyana",
|
||||
"countries.hn": "Honduras",
|
||||
"countries.hr": "Croatia",
|
||||
"countries.ht": "Haiti",
|
||||
"countries.hu": "Hungary",
|
||||
"countries.id": "Indonesia",
|
||||
"countries.dm": "Doiminice",
|
||||
"countries.dk": "An Danmhairg",
|
||||
"countries.do": "An Phoblacht Dhoiminiceach",
|
||||
"countries.dz": "An Ailgéir",
|
||||
"countries.ec": "Eacuadór",
|
||||
"countries.eg": "An Éigipt",
|
||||
"countries.er": "An Eiritré",
|
||||
"countries.es": "An Spáinn",
|
||||
"countries.ee": "An Eastóin",
|
||||
"countries.et": "An Aetóip",
|
||||
"countries.fi": "An Fhionlainn",
|
||||
"countries.fj": "Fidsí",
|
||||
"countries.fr": "An Fhrainc",
|
||||
"countries.fm": "An Mhicrinéis",
|
||||
"countries.ga": "An Ghabúin",
|
||||
"countries.gb": "An Ríocht Aontaithe",
|
||||
"countries.ge": "An tSeoirsia",
|
||||
"countries.gh": "Gána",
|
||||
"countries.gn": "An Ghuine",
|
||||
"countries.gm": "An Ghaimbia",
|
||||
"countries.gw": "Guine Bissau",
|
||||
"countries.gq": "An Ghuine Mheánchiorclach",
|
||||
"countries.gr": "An Ghréig",
|
||||
"countries.gd": "Greanáda",
|
||||
"countries.gt": "Guatamala",
|
||||
"countries.gy": "An Ghuáin",
|
||||
"countries.hn": "Hondúras",
|
||||
"countries.hr": "An Chróit",
|
||||
"countries.ht": "Háítí",
|
||||
"countries.hu": "An Ungáir",
|
||||
"countries.id": "An Indinéis",
|
||||
"countries.in": "India",
|
||||
"countries.ie": "Ireland",
|
||||
"countries.ir": "Iran",
|
||||
"countries.iq": "Iraq",
|
||||
"countries.is": "Iceland",
|
||||
"countries.il": "Israel",
|
||||
"countries.it": "Italy",
|
||||
"countries.jm": "Jamaica",
|
||||
"countries.jo": "Jordan",
|
||||
"countries.jp": "Japan",
|
||||
"countries.kz": "Kazakhstan",
|
||||
"countries.ke": "Kenya",
|
||||
"countries.kg": "Kyrgyzstan",
|
||||
"countries.kh": "Cambodia",
|
||||
"countries.ki": "Kiribati",
|
||||
"countries.kn": "Saint Kitts and Nevis",
|
||||
"countries.kr": "South Korea",
|
||||
"countries.kw": "Kuwait",
|
||||
"countries.ie": "Éire",
|
||||
"countries.ir": "An Iaráin",
|
||||
"countries.iq": "An Iaráic",
|
||||
"countries.is": "An Íoslainn",
|
||||
"countries.il": "Iosrael",
|
||||
"countries.it": "An Iodáil",
|
||||
"countries.jm": "Iamáice",
|
||||
"countries.jo": "An Iordáin",
|
||||
"countries.jp": "An tSeapáin",
|
||||
"countries.kz": "An Chasacstáin",
|
||||
"countries.ke": "An Chéinia",
|
||||
"countries.kg": "An Chirgeastáin",
|
||||
"countries.kh": "An Chambóid",
|
||||
"countries.ki": "Ciribeas",
|
||||
"countries.kn": "San Críostóir-Nimheas",
|
||||
"countries.kr": "An Chóiré Theas",
|
||||
"countries.kw": "Cuáit",
|
||||
"countries.la": "Laos",
|
||||
"countries.lb": "Lebanon",
|
||||
"countries.lr": "Liberia",
|
||||
"countries.ly": "Libya",
|
||||
"countries.lb": "An Liobáin",
|
||||
"countries.lr": "An Libéir",
|
||||
"countries.ly": "An Libia",
|
||||
"countries.lc": "Saint Lucia",
|
||||
"countries.li": "Liechtenstein",
|
||||
"countries.lk": "Sri Lanka",
|
||||
"countries.ls": "Lesotho",
|
||||
"countries.lt": "Lithuania",
|
||||
"countries.lu": "Luxembourg",
|
||||
"countries.lv": "Latvia",
|
||||
"countries.ma": "Morocco",
|
||||
"countries.mc": "Monaco",
|
||||
"countries.md": "Moldova",
|
||||
"countries.li": "Lichtinstéin",
|
||||
"countries.lk": "Srí Lanca",
|
||||
"countries.ls": "Leosóta",
|
||||
"countries.lt": "An Liotuáin",
|
||||
"countries.lu": "Lucsamburg",
|
||||
"countries.lv": "An Laitvia",
|
||||
"countries.ma": "Maracó",
|
||||
"countries.mc": "Monacó",
|
||||
"countries.md": "An Mholdóiv",
|
||||
"countries.mg": "Madagascar",
|
||||
"countries.mv": "Maldives",
|
||||
"countries.mx": "Mexico",
|
||||
"countries.mh": "Marshall Islands",
|
||||
"countries.mk": "Macedonia",
|
||||
"countries.ml": "Mali",
|
||||
"countries.mt": "Malta",
|
||||
"countries.mm": "Myanmar",
|
||||
"countries.me": "Montenegro",
|
||||
"countries.mn": "Mongolia",
|
||||
"countries.mz": "Mozambique",
|
||||
"countries.mr": "Mauritania",
|
||||
"countries.mu": "Mauritius",
|
||||
"countries.mw": "Malawi",
|
||||
"countries.my": "Malaysia",
|
||||
"countries.na": "Namibia",
|
||||
"countries.mv": "Oileáin Mhaildíve",
|
||||
"countries.mx": "Meicsiceo",
|
||||
"countries.mh": "Oileáin Marshall",
|
||||
"countries.mk": "An Mhacadóin",
|
||||
"countries.ml": "Mailí",
|
||||
"countries.mt": "Málta",
|
||||
"countries.mm": "Maenmar",
|
||||
"countries.me": "Montainéagró",
|
||||
"countries.mn": "An Mhongóil",
|
||||
"countries.mz": "Mósaimbíc",
|
||||
"countries.mr": "An Mháratáin",
|
||||
"countries.mu": "Oileán Mhuirís",
|
||||
"countries.mw": "Malávach",
|
||||
"countries.my": "An Mhalaeisia",
|
||||
"countries.na": "An Namaib",
|
||||
"countries.ne": "Niger",
|
||||
"countries.ng": "Nigeria",
|
||||
"countries.ni": "Nicaragua",
|
||||
"countries.nl": "Netherlands",
|
||||
"countries.no": "Norway",
|
||||
"countries.np": "Nepal",
|
||||
"countries.nr": "Nauru",
|
||||
"countries.nz": "New Zealand",
|
||||
"countries.om": "Oman",
|
||||
"countries.pk": "Pakistan",
|
||||
"countries.ng": "An Nigéir",
|
||||
"countries.ni": "Nicearagua",
|
||||
"countries.nl": "An Ísiltír",
|
||||
"countries.no": "An Iorua",
|
||||
"countries.np": "Neipeal",
|
||||
"countries.nr": "Nárú",
|
||||
"countries.nz": "An Nua-Shéalainn",
|
||||
"countries.om": "Óman",
|
||||
"countries.pk": "An Phacastáin",
|
||||
"countries.pa": "Panama",
|
||||
"countries.pe": "Peru",
|
||||
"countries.ph": "Philippines",
|
||||
"countries.pw": "Palau",
|
||||
"countries.pg": "Papua New Guinea",
|
||||
"countries.pl": "Poland",
|
||||
"countries.kp": "North Korea",
|
||||
"countries.pt": "Portugal",
|
||||
"countries.py": "Paraguay",
|
||||
"countries.qa": "Qatar",
|
||||
"countries.ro": "Romania",
|
||||
"countries.ru": "Russia",
|
||||
"countries.rw": "Rwanda",
|
||||
"countries.sa": "Saudi Arabia",
|
||||
"countries.sd": "Sudan",
|
||||
"countries.sn": "Senegal",
|
||||
"countries.sg": "Singapore",
|
||||
"countries.sb": "Solomon Islands",
|
||||
"countries.sl": "Sierra Leone",
|
||||
"countries.sv": "El Salvador",
|
||||
"countries.sm": "San Marino",
|
||||
"countries.so": "Somalia",
|
||||
"countries.rs": "Serbia",
|
||||
"countries.ss": "South Sudan",
|
||||
"countries.st": "São Tomé and Príncipe",
|
||||
"countries.sr": "Suriname",
|
||||
"countries.sk": "Slovakia",
|
||||
"countries.si": "Slovenia",
|
||||
"countries.se": "Sweden",
|
||||
"countries.sz": "Swaziland",
|
||||
"countries.sc": "Seychelles",
|
||||
"countries.sy": "Syria",
|
||||
"countries.td": "Chad",
|
||||
"countries.tg": "Togo",
|
||||
"countries.th": "Thailand",
|
||||
"countries.tj": "Tajikistan",
|
||||
"countries.tm": "Turkmenistan",
|
||||
"countries.tl": "Timor-Leste",
|
||||
"countries.pe": "Peiriú",
|
||||
"countries.ph": "Na hOileáin Fhilipíneacha",
|
||||
"countries.pw": "Oileáin Palau",
|
||||
"countries.pg": "Nua-Ghuine Phapua",
|
||||
"countries.pl": "An Pholainn",
|
||||
"countries.kp": "An Chóiré Thuaidh",
|
||||
"countries.pt": "An Phortaingéil",
|
||||
"countries.py": "Paragua",
|
||||
"countries.qa": "Catar",
|
||||
"countries.ro": "An Rómáin",
|
||||
"countries.ru": "An Rúis",
|
||||
"countries.rw": "Ruanda",
|
||||
"countries.sa": "An Araib Shádach",
|
||||
"countries.sd": "An tSúdáin",
|
||||
"countries.sn": "An tSeineagáil",
|
||||
"countries.sg": "Singeapór",
|
||||
"countries.sb": "Oileáin Sholaimh",
|
||||
"countries.sl": "Siarra Leon",
|
||||
"countries.sv": "An tSalvadóir",
|
||||
"countries.sm": "San Mairíne",
|
||||
"countries.so": "An tSomáil",
|
||||
"countries.rs": "An tSeirbia",
|
||||
"countries.ss": "An tSúdáin Theas",
|
||||
"countries.st": "São Tomé agus Príncipe",
|
||||
"countries.sr": "Suranam",
|
||||
"countries.sk": "An tSlóvaic",
|
||||
"countries.si": "An tSlóivéin",
|
||||
"countries.se": "An tSualainn",
|
||||
"countries.sz": "An tSuasalainn",
|
||||
"countries.sc": "Na Séiséil",
|
||||
"countries.sy": "An tSiria",
|
||||
"countries.td": "Sead",
|
||||
"countries.tg": "Tóga",
|
||||
"countries.th": "An Téalainn",
|
||||
"countries.tj": "An Táidsíceastáin",
|
||||
"countries.tm": "An Tuircméanastáin",
|
||||
"countries.tl": "Tíomór Thoir",
|
||||
"countries.to": "Tonga",
|
||||
"countries.tt": "Trinidad and Tobago",
|
||||
"countries.tn": "Tunisia",
|
||||
"countries.tr": "Turkey",
|
||||
"countries.tv": "Tuvalu",
|
||||
"countries.tz": "Tanzania",
|
||||
"countries.tt": "Oileán na Tríonóide agus Tobága",
|
||||
"countries.tn": "An Túinéis",
|
||||
"countries.tr": "An Tuirc",
|
||||
"countries.tv": "Túvalú",
|
||||
"countries.tz": "An Tansáin",
|
||||
"countries.ug": "Uganda",
|
||||
"countries.ua": "Ukraine",
|
||||
"countries.uy": "Uruguay",
|
||||
"countries.us": "United States",
|
||||
"countries.uz": "Uzbekistan",
|
||||
"countries.va": "Vatican City",
|
||||
"countries.vc": "Saint Vincent and the Grenadines",
|
||||
"countries.ve": "Venezuela",
|
||||
"countries.vn": "Vietnam",
|
||||
"countries.vu": "Vanuatu",
|
||||
"countries.ws": "Samoa",
|
||||
"countries.ye": "Yemen",
|
||||
"countries.za": "South Africa",
|
||||
"countries.zm": "Zambia",
|
||||
"countries.zw": "Zimbabwe",
|
||||
"continents.af": "Africa",
|
||||
"continents.an": "Antarctica",
|
||||
"continents.as": "Asia",
|
||||
"continents.eu": "Europe",
|
||||
"continents.na": "North America",
|
||||
"continents.oc": "Oceania",
|
||||
"continents.sa": "South America"
|
||||
"countries.ua": "An Úcráin",
|
||||
"countries.uy": "Uragua",
|
||||
"countries.us": "Na Stáit Aontaithe",
|
||||
"countries.uz": "An Úisbéiceastáin",
|
||||
"countries.va": "Cathair na Vatacáine",
|
||||
"countries.vc": "San Uinseann agus na Greanáidíní",
|
||||
"countries.ve": "Veiniséala",
|
||||
"countries.vn": "Vítneam",
|
||||
"countries.vu": "Vanuatú",
|
||||
"countries.ws": "Samó",
|
||||
"countries.ye": "Éimin",
|
||||
"countries.za": "An Afraic Theas",
|
||||
"countries.zm": "An tSaimbia",
|
||||
"countries.zw": "An tSiombáib",
|
||||
"continents.af": "An Afraic",
|
||||
"continents.an": "Antartaice",
|
||||
"continents.as": "An Áise",
|
||||
"continents.eu": "An Eoraip",
|
||||
"continents.na": "Meiriceá Thuaidh",
|
||||
"continents.oc": "An Aigéine",
|
||||
"continents.sa": "Meiriceá Theas"
|
||||
}
|
||||
|
|
|
@ -3,30 +3,30 @@
|
|||
"settings.locale": "jv",
|
||||
"settings.direction": "ltr",
|
||||
"emails.sender": "Tim %s",
|
||||
"emails.verification.subject": "",
|
||||
"emails.verification.hello": "",
|
||||
"emails.verification.body": "",
|
||||
"emails.verification.footer": "",
|
||||
"emails.verification.thanks": "",
|
||||
"emails.verification.signature": "",
|
||||
"emails.magicSession.subject": "",
|
||||
"emails.magicSession.hello": "",
|
||||
"emails.magicSession.body": "",
|
||||
"emails.magicSession.footer": "",
|
||||
"emails.magicSession.thanks": "",
|
||||
"emails.magicSession.signature": "",
|
||||
"emails.recovery.subject": "",
|
||||
"emails.recovery.hello": "",
|
||||
"emails.recovery.body": "",
|
||||
"emails.recovery.footer": "",
|
||||
"emails.recovery.thanks": "",
|
||||
"emails.recovery.signature": "",
|
||||
"emails.invitation.subject": "",
|
||||
"emails.invitation.hello": "",
|
||||
"emails.invitation.body": "",
|
||||
"emails.invitation.footer": "",
|
||||
"emails.invitation.thanks": "",
|
||||
"emails.invitation.signature": "",
|
||||
"emails.verification.subject": "Verifikasi Akun",
|
||||
"emails.verification.hello": "Hai {{name}}",
|
||||
"emails.verification.body": "Klik link iki kanggo verifikasi alamat email sampeyan.",
|
||||
"emails.verification.footer": "Yen sampeyan ora njaluk verifikasi alamat iki, sampeyan iso nglirwakake pesen iki.",
|
||||
"emails.verification.thanks": "Matur nuwun",
|
||||
"emails.verification.signature": "Tim {{project}}",
|
||||
"emails.magicSession.subject": "Masuk",
|
||||
"emails.magicSession.hello": "Hai,",
|
||||
"emails.magicSession.body": "Klik link iki kanggo masuk.",
|
||||
"emails.magicSession.footer": "Yen sampeyan ora njaluk masuk nggunakake alamat email iki, sampeyan iso nglirwakake pesen iki.",
|
||||
"emails.magicSession.thanks": "Matur nuwun",
|
||||
"emails.magicSession.signature": "Tim {{project}}",
|
||||
"emails.recovery.subject": "Setel ulang sandi",
|
||||
"emails.recovery.hello": "Halo {{name}}",
|
||||
"emails.recovery.body": "Klik link iki kanggo setel ulang sandi {{project}}.",
|
||||
"emails.recovery.footer": "Yen sampeyan ora njaluk setel ulang sandi, sampeyan iso nglirwakake pesen iki.",
|
||||
"emails.recovery.thanks": "Matur nuwun",
|
||||
"emails.recovery.signature": "Tim {{project}}",
|
||||
"emails.invitation.subject": "Undangan ke Tim %s di %s",
|
||||
"emails.invitation.hello": "Halo",
|
||||
"emails.invitation.body": "Email iki dikirim menyang sampeyan amarga {{owner}} pengin ngajak sampeyan dadi anggota tim {{team}} di {{project}}.",
|
||||
"emails.invitation.footer": "Yen sampeyan ora tertarik, sampeyan iso nglirwakake pesen iki.",
|
||||
"emails.invitation.thanks": "Matur nuwun",
|
||||
"emails.invitation.signature": "Tim {{project}}",
|
||||
"locale.country.unknown": "Ora dingerteni",
|
||||
"countries.af": "Afghanistan",
|
||||
"countries.ao": "Angola",
|
||||
|
|
|
@ -3,30 +3,30 @@
|
|||
"settings.locale": "mr",
|
||||
"settings.direction": "ltr",
|
||||
"emails.sender": "%s टीम",
|
||||
"emails.verification.subject": "",
|
||||
"emails.verification.hello": "",
|
||||
"emails.verification.body": "",
|
||||
"emails.verification.footer": "",
|
||||
"emails.verification.thanks": "",
|
||||
"emails.verification.signature": "",
|
||||
"emails.magicSession.subject": "",
|
||||
"emails.magicSession.hello": "",
|
||||
"emails.magicSession.body": "",
|
||||
"emails.magicSession.footer": "",
|
||||
"emails.magicSession.thanks": "",
|
||||
"emails.magicSession.signature": "",
|
||||
"emails.recovery.subject": "",
|
||||
"emails.recovery.hello": "",
|
||||
"emails.recovery.body": "",
|
||||
"emails.recovery.footer": "",
|
||||
"emails.recovery.thanks": "",
|
||||
"emails.recovery.signature": "",
|
||||
"emails.invitation.subject": "",
|
||||
"emails.invitation.hello": "",
|
||||
"emails.invitation.body": "",
|
||||
"emails.invitation.footer": "",
|
||||
"emails.invitation.thanks": "",
|
||||
"emails.invitation.signature": "",
|
||||
"emails.verification.subject": "खाते सत्यापन",
|
||||
"emails.verification.hello": "नमस्कार {{name}}",
|
||||
"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": "नमस्कार {{name}}",
|
||||
"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}} तुम्हाला {{project}} येथे {{team}} टीमचे सदस्य होण्यासाठी आमंत्रित करू इच्छित होते.",
|
||||
"emails.invitation.footer": "आपल्याला स्वारस्य नसल्यास, आपण या संदेशाकडे दुर्लक्ष करू शकता.",
|
||||
"emails.invitation.thanks": "धन्यवाद",
|
||||
"emails.invitation.signature": "{{project}} संघ",
|
||||
"locale.country.unknown": "अज्ञात",
|
||||
"countries.af": "अफगानिस्तान",
|
||||
"countries.ao": "अंगोला",
|
||||
|
|
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
|
@ -23,6 +23,7 @@ use Utopia\Database\Validator\UID;
|
|||
use Utopia\Exception;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
|
@ -217,6 +218,8 @@ App::post('/v1/account/sessions')
|
|||
->setParam('userId', $profile->getId())
|
||||
->setParam('event', 'account.sessions.create')
|
||||
->setParam('resource', 'user/' . $profile->getId())
|
||||
->setParam('userEmail', $profile->getAttribute('email', ''))
|
||||
->setParam('userName', $profile->getAttribute('name', ''))
|
||||
;
|
||||
|
||||
if (!Config::getParam('domainVerification')) {
|
||||
|
@ -1182,13 +1185,15 @@ App::get('/v1/account/logs')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('dbForInternal')
|
||||
->inject('usage')
|
||||
->action(function ($response, $user, $locale, $geodb, $dbForInternal, $usage) {
|
||||
->action(function ($limit, $offset, $response, $user, $locale, $geodb, $dbForInternal, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
|
@ -1198,8 +1203,7 @@ App::get('/v1/account/logs')
|
|||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
$audit = new Audit($dbForInternal);
|
||||
|
||||
$logs = $audit->getLogsByUserAndEvents($user->getId(), [
|
||||
$auditEvents = [
|
||||
'account.create',
|
||||
'account.delete',
|
||||
'account.update.name',
|
||||
|
@ -1215,7 +1219,9 @@ App::get('/v1/account/logs')
|
|||
'teams.membership.create',
|
||||
'teams.membership.update',
|
||||
'teams.membership.delete',
|
||||
]);
|
||||
];
|
||||
|
||||
$logs = $audit->getLogsByUserAndEvents($user->getId(), $auditEvents, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
||||
|
@ -1224,17 +1230,19 @@ App::get('/v1/account/logs')
|
|||
|
||||
$detector = new Detector($log['userAgent']);
|
||||
|
||||
$output[$i] = new Document(array_merge([
|
||||
'event' => $log['event'],
|
||||
'ip' => $log['ip'],
|
||||
'time' => $log['time'],
|
||||
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
|
||||
$output[$i] = new Document(array_merge(
|
||||
$log->getArrayCopy(),
|
||||
$log['data'],
|
||||
$detector->getOS(),
|
||||
$detector->getClient(),
|
||||
$detector->getDevice()
|
||||
));
|
||||
|
||||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
|
@ -1245,7 +1253,11 @@ App::get('/v1/account/logs')
|
|||
$usage
|
||||
->setParam('users.read', 1)
|
||||
;
|
||||
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'sum' => $audit->countLogsByUserAndEvents($user->getId(), $auditEvents),
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/:sessionId')
|
||||
|
|
|
@ -40,13 +40,14 @@ use DeviceDetector\DeviceDetector;
|
|||
* @param Utopia\Database\Document $attribute
|
||||
* @param Appwrite\Utopia\Response $response
|
||||
* @param Utopia\Database\Database $dbForInternal
|
||||
* @param Utopia\Database\Database $dbForExternal
|
||||
* @param Appwrite\Event\Event $database
|
||||
* @param Appwrite\Event\Event $audits
|
||||
* @param Appwrite\Stats\Stats $usage
|
||||
*
|
||||
* @return Document Newly created attribute document
|
||||
*/
|
||||
function createAttribute($collectionId, $attribute, $response, $dbForInternal, $database, $audits, $usage): Document
|
||||
function createAttribute($collectionId, $attribute, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage): Document
|
||||
{
|
||||
$attributeId = $attribute->getId();
|
||||
$type = $attribute->getAttribute('type', '');
|
||||
|
@ -94,6 +95,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
|
|||
'array' => $array,
|
||||
'format' => $format,
|
||||
'formatOptions' => $formatOptions,
|
||||
'filters' => $filters,
|
||||
]);
|
||||
|
||||
$dbForInternal->checkAttribute($collection, $attribute);
|
||||
|
@ -107,6 +109,7 @@ function createAttribute($collectionId, $attribute, $response, $dbForInternal, $
|
|||
}
|
||||
|
||||
$dbForInternal->deleteCachedDocument('collections', $collectionId);
|
||||
$dbForExternal->deleteCachedCollection($collectionId);
|
||||
|
||||
// Pass clone of $attribute object to workers
|
||||
// so we can later modify Document to fit response model
|
||||
|
@ -287,7 +290,7 @@ App::get('/v1/database/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
@ -321,13 +324,16 @@ App::get('/v1/database/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
@ -335,22 +341,38 @@ App::get('/v1/database/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
// TODO@kodumbeats explore performance if query is ordered by time ASC
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'documents.count' => $stats["database.documents.count"],
|
||||
'collections.count' => $stats["database.collections.count"],
|
||||
'documents.create' => $stats["database.documents.create"],
|
||||
'documents.read' => $stats["database.documents.read"],
|
||||
'documents.update' => $stats["database.documents.update"],
|
||||
'documents.delete' => $stats["database.documents.delete"],
|
||||
'collections.create' => $stats["database.collections.create"],
|
||||
'collections.read' => $stats["database.collections.read"],
|
||||
'collections.update' => $stats["database.collections.update"],
|
||||
'collections.delete' => $stats["database.collections.delete"],
|
||||
'documentsCount' => $stats["database.documents.count"],
|
||||
'collectionsCount' => $stats["database.collections.count"],
|
||||
'documentsCreate' => $stats["database.documents.create"],
|
||||
'documentsRead' => $stats["database.documents.read"],
|
||||
'documentsUpdate' => $stats["database.documents.update"],
|
||||
'documentsDelete' => $stats["database.documents.delete"],
|
||||
'collectionsCreate' => $stats["database.collections.create"],
|
||||
'collectionsRead' => $stats["database.collections.read"],
|
||||
'collectionsUpdate' => $stats["database.collections.update"],
|
||||
'collectionsDelete' => $stats["database.collections.delete"],
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -386,7 +408,7 @@ App::get('/v1/database/:collectionId/usage')
|
|||
|
||||
$usage = [];
|
||||
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
@ -415,13 +437,16 @@ App::get('/v1/database/:collectionId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
@ -429,17 +454,32 @@ App::get('/v1/database/:collectionId/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'documents.count' => $stats["database.collections.$collectionId.documents.count"],
|
||||
'documents.create' => $stats["database.collections.$collectionId.documents.create"],
|
||||
'documents.read' => $stats["database.collections.$collectionId.documents.read"],
|
||||
'documents.update' => $stats["database.collections.$collectionId.documents.update"],
|
||||
'documents.delete' => $stats["database.collections.$collectionId.documents.delete"]
|
||||
'documentsCount' => $stats["database.collections.$collectionId.documents.count"],
|
||||
'documentsCreate' => $stats["database.collections.$collectionId.documents.create"],
|
||||
'documentsRead' => $stats["database.collections.$collectionId.documents.read"],
|
||||
'documentsUpdate' => $stats["database.collections.$collectionId.documents.update"],
|
||||
'documentsDelete' => $stats["database.collections.$collectionId.documents.delete"]
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -458,12 +498,14 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('collectionId', '', new UID(), 'Collection unique ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($collectionId, $response, $dbForInternal, $dbForExternal, $locale, $geodb) {
|
||||
->action(function ($collectionId, $limit, $offset, $response, $dbForInternal, $dbForExternal, $locale, $geodb) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
|
@ -478,8 +520,8 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
}
|
||||
|
||||
$audit = new Audit($dbForInternal);
|
||||
|
||||
$logs = $audit->getLogsByResource('collection/'.$collection->getId());
|
||||
$resource = 'collection/'.$collection->getId();
|
||||
$logs = $audit->getLogsByResource($resource, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
||||
|
@ -539,7 +581,10 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
}
|
||||
}
|
||||
|
||||
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
|
||||
$response->dynamic(new Document([
|
||||
'sum' => $audit->countLogsByResource($resource),
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::put('/v1/database/collections/:collectionId')
|
||||
|
@ -643,6 +688,8 @@ App::delete('/v1/database/collections/:collectionId')
|
|||
throw new Exception('Failed to remove collection from DB', 500);
|
||||
}
|
||||
|
||||
$dbForExternal->deleteCachedCollection($collection->getId());
|
||||
|
||||
$deletes
|
||||
->setParam('type', DELETE_TYPE_DOCUMENT)
|
||||
->setParam('document', $collection)
|
||||
|
@ -683,12 +730,14 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $size, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal*/
|
||||
/** @var Utopia\Database\Database $dbForExternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
@ -706,7 +755,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
|
|||
'required' => $required,
|
||||
'default' => $default,
|
||||
'array' => $array,
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING);
|
||||
});
|
||||
|
@ -730,12 +779,14 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal*/
|
||||
/** @var Utopia\Database\Database $dbForExternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
@ -748,7 +799,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
|
|||
'default' => $default,
|
||||
'array' => $array,
|
||||
'format' => APP_DATABASE_ATTRIBUTE_EMAIL,
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL);
|
||||
});
|
||||
|
@ -773,12 +824,14 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $elements, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $elements, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal*/
|
||||
/** @var Utopia\Database\Database $dbForExternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
@ -803,7 +856,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
|
|||
'array' => $array,
|
||||
'format' => APP_DATABASE_ATTRIBUTE_ENUM,
|
||||
'formatOptions' => ['elements' => $elements],
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM);
|
||||
});
|
||||
|
@ -827,12 +880,14 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal*/
|
||||
/** @var Utopia\Database\Database $dbForExternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
@ -845,7 +900,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
|
|||
'default' => $default,
|
||||
'array' => $array,
|
||||
'format' => APP_DATABASE_ATTRIBUTE_IP,
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP);
|
||||
});
|
||||
|
@ -869,10 +924,11 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForExternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
|
@ -887,7 +943,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
|
|||
'default' => $default,
|
||||
'array' => $array,
|
||||
'format' => APP_DATABASE_ATTRIBUTE_URL,
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL);
|
||||
});
|
||||
|
@ -913,12 +969,14 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal*/
|
||||
/** @var Utopia\Database\Database $dbForExternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
@ -949,7 +1007,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
|
|||
'min' => $min,
|
||||
'max' => $max,
|
||||
],
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
|
||||
|
@ -982,18 +1040,20 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $required, $min, $max, $default, $array, $response, $dbForInternal, $dbForExternal,$database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal*/
|
||||
/** @var Utopia\Database\Database $dbForExternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
|
||||
// Ensure attribute default is within range
|
||||
$min = (is_null($min)) ? PHP_FLOAT_MIN : \floatval($min);
|
||||
$min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min);
|
||||
$max = (is_null($max)) ? PHP_FLOAT_MAX : \floatval($max);
|
||||
|
||||
if ($min > $max) {
|
||||
|
@ -1018,7 +1078,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
|||
'min' => $min,
|
||||
'max' => $max,
|
||||
],
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
|
||||
|
@ -1049,10 +1109,11 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
|
|||
->param('array', false, new Boolean(), 'Is attribute an array?', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $database, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $required, $default, $array, $response, $dbForInternal, $dbForExternal, $database, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal*/
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
|
@ -1066,7 +1127,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
|
|||
'required' => $required,
|
||||
'default' => $default,
|
||||
'array' => $array,
|
||||
]), $response, $dbForInternal, $database, $audits, $usage);
|
||||
]), $response, $dbForInternal, $dbForExternal, $database, $audits, $usage);
|
||||
|
||||
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN);
|
||||
});
|
||||
|
@ -1184,13 +1245,15 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
|
|||
->param('attributeId', '', new Key(), 'Attribute ID.')
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('database')
|
||||
->inject('events')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $database, $events, $audits, $usage) {
|
||||
->action(function ($collectionId, $attributeId, $response, $dbForInternal, $dbForExternal, $database, $events, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
/** @var Utopia\Database\Database $dbForExternal */
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Event $audits */
|
||||
|
@ -1214,6 +1277,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
|
|||
}
|
||||
|
||||
$dbForInternal->deleteCachedDocument('collections', $collectionId);
|
||||
$dbForExternal->deleteCachedCollection($collection->getId());
|
||||
|
||||
$database
|
||||
->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE)
|
||||
|
@ -1654,11 +1718,11 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
return Query::parse($query);
|
||||
}, $queries);
|
||||
|
||||
// TODO@kodumbeats use strict query validation
|
||||
$validator = new QueriesValidator(new QueryValidator($collection->getAttribute('attributes', [])), $collection->getAttribute('indexes', []), false);
|
||||
|
||||
if (!$validator->isValid($queries)) {
|
||||
throw new Exception($validator->getDescription(), 400);
|
||||
if (!empty($queries)) {
|
||||
$validator = new QueriesValidator(new QueryValidator($collection->getAttribute('attributes', [])), $collection->getAttribute('indexes', []), true);
|
||||
if (!$validator->isValid($queries)) {
|
||||
throw new Exception($validator->getDescription(), 400);
|
||||
}
|
||||
}
|
||||
|
||||
$cursorDocument = null;
|
||||
|
@ -1747,6 +1811,113 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
$response->dynamic($document, Response::MODEL_DOCUMENT);
|
||||
});
|
||||
|
||||
App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
|
||||
->desc('List Document Logs')
|
||||
->groups(['api', 'database'])
|
||||
->label('scope', 'documents.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
|
||||
->label('sdk.namespace', 'database')
|
||||
->label('sdk.method', 'listDocumentLogs')
|
||||
->label('sdk.description', '/docs/references/database/get-document-logs.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('collectionId', '', new UID(), 'Collection unique ID.')
|
||||
->param('documentId', null, new UID(), 'Document unique ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('dbForExternal')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($collectionId, $documentId, $limit, $offset, $response, $dbForInternal, $dbForExternal, $locale, $geodb) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
/** @var Utopia\Database\Database $dbForExternal */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
|
||||
$collection = $dbForInternal->getDocument('collections', $collectionId);
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Collection not found', 404);
|
||||
}
|
||||
|
||||
$document = $dbForExternal->getDocument($collectionId, $documentId);
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('No document found', 404);
|
||||
}
|
||||
|
||||
$audit = new Audit($dbForInternal);
|
||||
$resource = 'document/'.$document->getId();
|
||||
$logs = $audit->getLogsByResource($resource, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
||||
foreach ($logs as $i => &$log) {
|
||||
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
|
||||
|
||||
$dd = new DeviceDetector($log['userAgent']);
|
||||
|
||||
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
|
||||
|
||||
$dd->parse();
|
||||
|
||||
$os = $dd->getOs();
|
||||
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
|
||||
$osName = (isset($os['name'])) ? $os['name'] : '';
|
||||
$osVersion = (isset($os['version'])) ? $os['version'] : '';
|
||||
|
||||
$client = $dd->getClient();
|
||||
$clientType = (isset($client['type'])) ? $client['type'] : '';
|
||||
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
|
||||
$clientName = (isset($client['name'])) ? $client['name'] : '';
|
||||
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
|
||||
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
|
||||
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
|
||||
|
||||
$output[$i] = new Document([
|
||||
'event' => $log['event'],
|
||||
'userId' => $log['userId'],
|
||||
'userEmail' => $log['data']['userEmail'] ?? null,
|
||||
'userName' => $log['data']['userName'] ?? null,
|
||||
'mode' => $log['data']['mode'] ?? null,
|
||||
'ip' => $log['ip'],
|
||||
'time' => $log['time'],
|
||||
|
||||
'osCode' => $osCode,
|
||||
'osName' => $osName,
|
||||
'osVersion' => $osVersion,
|
||||
'clientType' => $clientType,
|
||||
'clientCode' => $clientCode,
|
||||
'clientName' => $clientName,
|
||||
'clientVersion' => $clientVersion,
|
||||
'clientEngine' => $clientEngine,
|
||||
'clientEngineVersion' => $clientEngineVersion,
|
||||
'deviceName' => $dd->getDeviceName(),
|
||||
'deviceBrand' => $dd->getBrandName(),
|
||||
'deviceModel' => $dd->getModel(),
|
||||
]);
|
||||
|
||||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
}
|
||||
}
|
||||
$response->dynamic(new Document([
|
||||
'sum' => $audit->countLogsByResource($resource),
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
||||
->desc('Update Document')
|
||||
->groups(['api', 'database'])
|
||||
|
@ -1901,6 +2072,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
}
|
||||
|
||||
$dbForExternal->deleteDocument($collectionId, $documentId);
|
||||
$dbForExternal->deleteCachedDocument($collectionId, $documentId);
|
||||
|
||||
$usage
|
||||
->setParam('database.documents.delete', 1)
|
||||
|
|
|
@ -176,7 +176,7 @@ App::get('/v1/functions/:functionId/usage')
|
|||
|
||||
$usage = [];
|
||||
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
@ -203,12 +203,15 @@ App::get('/v1/functions/:functionId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
|
@ -217,15 +220,30 @@ App::get('/v1/functions/:functionId/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'functions.executions' => $stats["functions.$functionId.executions"],
|
||||
'functions.failures' => $stats["functions.$functionId.failures"],
|
||||
'functions.compute' => $stats["functions.$functionId.compute"]
|
||||
'functionsExecutions' => $stats["functions.$functionId.executions"],
|
||||
'functionsFailures' => $stats["functions.$functionId.failures"],
|
||||
'functionsCompute' => $stats["functions.$functionId.compute"]
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
@ -288,13 +288,16 @@ App::get('/v1/projects/:projectId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
@ -302,8 +305,23 @@ App::get('/v1/projects/:projectId/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
|
|
|
@ -1728,7 +1728,7 @@ App::get('/v1/storage/usage')
|
|||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
|
@ -1839,7 +1839,7 @@ App::get('/v1/storage/:bucketId/usage')
|
|||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
|
|
|
@ -259,12 +259,14 @@ App::get('/v1/users/:userId/logs')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. Use this value to manage pagination. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, 900000000), 'Offset value. The default value is 0. Use this param to manage pagination.', true)
|
||||
->inject('response')
|
||||
->inject('dbForInternal')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $response, $dbForInternal, $locale, $geodb, $usage) {
|
||||
->action(function ($userId, $limit, $offset, $response, $dbForInternal, $locale, $geodb, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
|
@ -279,8 +281,7 @@ App::get('/v1/users/:userId/logs')
|
|||
}
|
||||
|
||||
$audit = new Audit($dbForInternal);
|
||||
|
||||
$logs = $audit->getLogsByUserAndEvents($user->getId(), [
|
||||
$auditEvents = [
|
||||
'account.create',
|
||||
'account.delete',
|
||||
'account.update.name',
|
||||
|
@ -296,7 +297,9 @@ App::get('/v1/users/:userId/logs')
|
|||
'teams.membership.create',
|
||||
'teams.membership.update',
|
||||
'teams.membership.delete',
|
||||
]);
|
||||
];
|
||||
|
||||
$logs = $audit->getLogsByUserAndEvents($user->getId(), $auditEvents, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
||||
|
@ -355,7 +358,11 @@ App::get('/v1/users/:userId/logs')
|
|||
$usage
|
||||
->setParam('users.read', 1)
|
||||
;
|
||||
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'sum' => $audit->countLogsByUserAndEvents($user->getId(), $auditEvents),
|
||||
'logs' => $output,
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/status')
|
||||
|
@ -780,7 +787,7 @@ App::get('/v1/users/usage')
|
|||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$period = [
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
'limit' => 48,
|
||||
|
@ -812,12 +819,15 @@ App::get('/v1/users/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForInternal, $period, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function() use ($dbForInternal, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
||||
$requestDocs = $dbForInternal->find('stats', [
|
||||
new Query('period', Query::TYPE_EQUAL, [$period[$range]['period']]),
|
||||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $period[$range]['limit'], 0, ['time'], [Database::ORDER_DESC]);
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
|
@ -826,20 +836,36 @@ App::get('/v1/users/usage')
|
|||
'date' => $requestDoc->getAttribute('time'),
|
||||
];
|
||||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
$stats[$metric][] = [
|
||||
'value' => 0,
|
||||
'date' => ($stats[$metric][$last]['date'] ?? \time()) - $diff, // time of last metric minus period
|
||||
];
|
||||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
'range' => $range,
|
||||
'users.count' => $stats["users.count"],
|
||||
'users.create' => $stats["users.create"],
|
||||
'users.read' => $stats["users.read"],
|
||||
'users.update' => $stats["users.update"],
|
||||
'users.delete' => $stats["users.delete"],
|
||||
'sessions.create' => $stats["users.sessions.create"],
|
||||
'sessions.provider.create' => $stats["users.sessions.$provider.create"],
|
||||
'sessions.delete' => $stats["users.sessions.delete"]
|
||||
'usersCount' => $stats["users.count"],
|
||||
'usersCreate' => $stats["users.create"],
|
||||
'usersRead' => $stats["users.read"],
|
||||
'usersUpdate' => $stats["users.update"],
|
||||
'usersDelete' => $stats["users.delete"],
|
||||
'sessionsCreate' => $stats["users.sessions.create"],
|
||||
'sessionsProviderCreate' => $stats["users.sessions.$provider.create"],
|
||||
'sessionsDelete' => $stats["users.sessions.delete"]
|
||||
]);
|
||||
|
||||
}
|
||||
|
|
|
@ -218,6 +218,10 @@ App::get('/console/database/collection')
|
|||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
->setParam('method', 'database.listCollectionLogs')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.id}}',
|
||||
])
|
||||
;
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
|
||||
|
@ -245,14 +249,45 @@ App::get('/console/database/document')
|
|||
->action(function ($collection, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
->setParam('method', 'database.listDocumentLogs')
|
||||
->setParam('params', [
|
||||
'collection-id' => '{{router.params.collection}}',
|
||||
'document-id' => '{{router.params.id}}',
|
||||
])
|
||||
;
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
$searchFiles = new View(__DIR__.'/../../views/console/database/search/files.phtml');
|
||||
$searchDocuments = new View(__DIR__.'/../../views/console/database/search/documents.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', false)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('searchFiles', $searchFiles)
|
||||
->setParam('searchDocuments', $searchDocuments)
|
||||
->setParam('logs', $logs)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Document')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
App::get('/console/database/document/new')
|
||||
->groups(['web', 'console'])
|
||||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Utopia\View $layout */
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', true)
|
||||
->setParam('collection', $collection)
|
||||
->setParam('logs', new View())
|
||||
;
|
||||
|
||||
$layout
|
||||
|
|
|
@ -280,17 +280,19 @@ $cli
|
|||
$max = 10;
|
||||
$sleep = 1;
|
||||
|
||||
$database = $client->selectDB('telegraf');
|
||||
do { // check if telegraf database is ready
|
||||
$attempts++;
|
||||
if(!in_array('telegraf', $client->listDatabases())) {
|
||||
try {
|
||||
$attempts++;
|
||||
$database = $client->selectDB('telegraf');
|
||||
if(in_array('telegraf', $client->listDatabases())) {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("InfluxDB not ready. Retrying connection ({$attempts})...");
|
||||
if($attempts >= $max) {
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('InfluxDB database not ready yet');
|
||||
}
|
||||
sleep($sleep);
|
||||
} else {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} while ($attempts < $max);
|
||||
|
||||
|
@ -309,17 +311,20 @@ $cli
|
|||
$filters = $options['filters'] ?? []; // Some metrics might have additional filters, like function's status
|
||||
if (!empty($filters)) {
|
||||
$filters = ' AND ' . implode(' AND ', array_map(function ($filter, $value) {
|
||||
return '"' . $filter . '"=\'' . $value . '\'';
|
||||
return "\"{$filter}\"='{$value}'";
|
||||
}, array_keys($filters), array_values($filters)));
|
||||
} else {
|
||||
$filters = '';
|
||||
}
|
||||
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "' . $table . '" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\'' . (empty($filters) ? '' : $filters) . ' GROUP BY time(' . $period['key'] . '), "projectId"' . $groupBy . ' FILL(null)');
|
||||
$query = "SELECT sum(value) AS \"value\" FROM \"{$table}\" WHERE \"time\" > '{$start}' AND \"time\" < '{$end}' AND \"metric_type\"='counter' {$filters} GROUP BY time({$period['key']}), \"projectId\" {$groupBy} FILL(null)";
|
||||
$result = $database->query($query);
|
||||
|
||||
$points = $result->getPoints();
|
||||
foreach ($points as $point) {
|
||||
$projectId = $point['projectId'];
|
||||
|
||||
if (!empty($projectId) && $projectId != 'console') {
|
||||
if (!empty($projectId) && $projectId !== 'console') {
|
||||
$dbForProject->setNamespace('project_' . $projectId . '_internal');
|
||||
$metricUpdated = $metric;
|
||||
|
||||
|
@ -347,8 +352,11 @@ $cli
|
|||
'type' => 0,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $value));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $value)
|
||||
);
|
||||
}
|
||||
$latestTime[$metric][$period['key']] = $time;
|
||||
} catch (\Exception $e) { // if projects are deleted this might fail
|
||||
|
@ -364,7 +372,7 @@ $cli
|
|||
* Aggregate MariaDB every 15 minutes
|
||||
* Some of the queries here might contain full-table scans.
|
||||
*/
|
||||
if ($iterations % 30 == 0) { // Every 15 minutes aggregate number of objects in database
|
||||
if ($iterations % 30 === 0) { // Every 15 minutes aggregate number of objects in database
|
||||
|
||||
$latestProject = null;
|
||||
|
||||
|
@ -376,7 +384,7 @@ $cli
|
|||
do { // list projects
|
||||
try {
|
||||
$attempts++;
|
||||
$projects = $dbForConsole->find('projects', [], 100, cursor:$latestProject);
|
||||
$projects = $dbForConsole->find('projects', [], 100, cursor: $latestProject);
|
||||
break; // leave the do-while if successful
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Console DB not ready yet. Retrying ({$attempts})...");
|
||||
|
@ -413,10 +421,13 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $storageTotal));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
$id = \md5($time . '_1d_storage.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
|
@ -430,8 +441,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $storageTotal));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $storageTotal)
|
||||
);
|
||||
}
|
||||
|
||||
$collections = [
|
||||
|
@ -483,8 +497,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
@ -500,8 +517,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$subCollections = $options['subCollections'] ?? [];
|
||||
|
@ -516,7 +536,7 @@ $cli
|
|||
|
||||
do { // Loop over all the parent collection document for each sub collection
|
||||
$dbForProject->setNamespace("project_{$projectId}_{$options['namespace']}");
|
||||
$parents = $dbForProject->find($collection, [], 100, cursor:$latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||
$parents = $dbForProject->find($collection, [], 100, cursor: $latestParent); // Get all the parents for the sub collections for example for documents, this will get all the collections
|
||||
|
||||
if (empty($parents)) {
|
||||
continue;
|
||||
|
@ -547,8 +567,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
@ -564,8 +587,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
// check if sum calculation is required
|
||||
|
@ -641,8 +667,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
@ -658,8 +687,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -301,7 +301,7 @@
|
|||
data-name="securityLogs"
|
||||
data-event="load">
|
||||
|
||||
<div class="box">
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical small">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -330,6 +330,33 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="account.getLogs"
|
||||
data-event="submit"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-scope="console"
|
||||
data-name="securityLogs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{securityLogs.sum}}" class="margin-end round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{securityLogs.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="account.getLogs"
|
||||
data-event="submit"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-offset="{{router.params.offset}}"
|
||||
data-scope="console"
|
||||
data-name="securityLogs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{securityLogs.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -211,8 +211,9 @@
|
|||
required
|
||||
maxlength="36"
|
||||
class=""
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
name="projectId" />
|
||||
|
||||
|
||||
<label>Name</label>
|
||||
<input type="text" class="full-width margin-bottom-xl" name="name" required autocomplete="off" maxlength="128" />
|
||||
|
||||
|
|
|
@ -1,78 +1,96 @@
|
|||
<?php
|
||||
$interval = floor((int)$this->getParam('interval', 0) / 86400);
|
||||
$method = $this->getParam('method', '');
|
||||
$params = $this->getParam('params', []);
|
||||
?>
|
||||
<div
|
||||
data-service="database.listCollectionLogs"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-service="<?php echo $method; ?>"
|
||||
<?php foreach($params as $key => $value): ?>
|
||||
data-param-<?php echo $key; ?>="<?php echo $value; ?>"
|
||||
<?php endforeach; ?>
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-scope="sdk"
|
||||
data-event="load"
|
||||
data-name="project-collection-logs">
|
||||
|
||||
<div class="box margin-bottom">
|
||||
<div data-ls-if="0 == {{project-collection-logs.logs.length}}">
|
||||
data-name="logs">
|
||||
<div data-ls-if="0 == {{logs.logs.length}}">
|
||||
<div class="box margin-bottom">
|
||||
<h3 class="margin-bottom-small text-bold">No Logs Found</h3>
|
||||
|
||||
<p class="margin-bottom-no">Logs are retained for <?php echo $this->escape($interval); ?> days.</p>
|
||||
</div>
|
||||
|
||||
<table class="vertical small" data-ls-if="0 != {{project-collection-logs.logs.length}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="120">Date</th>
|
||||
<th width="180">Initiator</th>
|
||||
<th>Event</th>
|
||||
<th width="110">Location</th>
|
||||
<th width="90">IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="project-collection-logs.logs" data-ls-as="log" class="text-size-small">
|
||||
<tr>
|
||||
<td data-title="Date: "><span class="text-fade" data-ls-bind="{{log.time|dateTime}}"></span></td>
|
||||
<td data-title="Initiator: ">
|
||||
<span data-ls-if="{{log.userName|escape}} !== '' && {{log.mode}} === ''"><i class="icon-user"></i> <a data-ls-attrs="href=/console/users/user?id={{log.userId}}&project={{router.params.project}}" data-ls-bind="{{log.userName}}"></a></span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} !== '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Unknown</span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} === '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Anonymous User</span>
|
||||
<span data-ls-if="{{log.mode}} === 'admin'">
|
||||
<img src="" data-ls-attrs="src={{log.userName|avatar}}" data-size="45" alt="User Avatar" class="avatar xxs inline margin-end-small" loading="lazy" width="30" height="30" /> <span data-ls-bind="{{log.userName}}"></span> <span class="text-fade text-size-xs">(Admin)</span>
|
||||
</span>
|
||||
<span data-ls-if="{{log.mode}} === 'key'"> <i class="icon-key"></i> API Key</span>
|
||||
</td>
|
||||
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
|
||||
<td data-title="Location: ">
|
||||
<img onerror="this.onerror=null;this.className='avatar xxs hide'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
|
||||
<span data-ls-bind="{{log.countryName}}"></span>
|
||||
</td>
|
||||
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="database.listCollectionLogs"
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-collection-logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-collection-logs.sum}}" class="margin-end-small round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
<div data-ls-if="0 != {{logs.sum}}">
|
||||
<div class="margin-bottom-small margin-top-negative text-align-end text-size-small text-fade">Showing logs from the last <?php echo $this->escape($interval); ?> days</div>
|
||||
|
||||
<form
|
||||
data-service="database.listCollectionLogs"
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="project-collection-logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-collection-logs.sum}}" class="margin-start-small round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="box margin-bottom">
|
||||
<table class="vertical small">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="120">Date</th>
|
||||
<th width="180">By</th>
|
||||
<th>Event</th>
|
||||
<th width="110">Location</th>
|
||||
<th width="90">IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-ls-loop="logs.logs" data-ls-as="log" class="text-size-small">
|
||||
<tr>
|
||||
<td data-title="Date: "><span class="text-fade" data-ls-bind="{{log.time|dateTime}}"></span></td>
|
||||
<td data-title="By: ">
|
||||
<span data-ls-if="{{log.userName|escape}} !== '' && {{log.mode}} === ''"><i class="icon-user"></i> <a data-ls-attrs="href=/console/users/user?id={{log.userId}}&project={{router.params.project}}" data-ls-bind="{{log.userName}}"></a></span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} !== '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Unknown</span>
|
||||
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} === '' && {{log.mode}} !== 'key'"><i class="icon-user"></i> Anonymous User</span>
|
||||
<span data-ls-if="{{log.mode}} === 'admin'">
|
||||
<img src="" data-ls-attrs="src={{log.userName|avatar}}" data-size="45" alt="User Avatar" class="avatar xxs inline margin-end-small" loading="lazy" width="30" height="30" /> <span data-ls-bind="{{log.userName}}"></span> <span class="text-fade text-size-xs">(Admin)</span>
|
||||
</span>
|
||||
<span data-ls-if="{{log.mode}} === 'key'"> <i class="icon-key"></i> API Key</span>
|
||||
</td>
|
||||
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
|
||||
<td data-title="Location: ">
|
||||
<img onerror="this.onerror=null;this.className='avatar xxs hide'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
|
||||
<span data-ls-bind="{{log.countryName}}"></span>
|
||||
</td>
|
||||
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="pull-end text-align-center paging">
|
||||
<form
|
||||
data-service="<?php echo $method; ?>"
|
||||
<?php foreach($params as $key => $value): ?>
|
||||
data-param-<?php echo $key; ?>="<?php echo $value; ?>"
|
||||
<?php endforeach; ?>
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{logs.sum}}" class="margin-end-small round small" aria-label="Back"><i class="icon-left-open"></i></button>
|
||||
</form>
|
||||
|
||||
<span data-ls-bind="{{router.params.offset|pageCurrent}} / {{logs.sum|pageTotal}}"></span>
|
||||
|
||||
<form
|
||||
data-service="<?php echo $method; ?>"
|
||||
<?php foreach($params as $key => $value): ?>
|
||||
data-param-<?php echo $key; ?>="<?php echo $value; ?>"
|
||||
<?php endforeach; ?>
|
||||
data-event="submit"
|
||||
data-param-collection-id="{{router.params.id}}"
|
||||
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
|
||||
data-param-order-type="DESC"
|
||||
data-scope="sdk"
|
||||
data-name="logs"
|
||||
data-success="state"
|
||||
data-success-param-state-keys="search,offset">
|
||||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{logs.sum}}" class="margin-start-small round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,16 @@
|
|||
<?php
|
||||
|
||||
$new = $this->getParam('new', false);
|
||||
$logs = $this->getParam('logs', null);
|
||||
|
||||
?>
|
||||
<div
|
||||
data-service="database.getCollection"
|
||||
data-param-collection-id="{{router.params.collection}}"
|
||||
data-scope="sdk"
|
||||
data-event="load,database.updateDocument"
|
||||
data-name="project-collection">
|
||||
|
||||
|
||||
<div
|
||||
data-service="database.getDocument"
|
||||
data-param-collection-id="{{router.params.collection}}"
|
||||
|
@ -27,7 +33,7 @@
|
|||
|
||||
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
|
||||
<h2>JSON View</h2>
|
||||
|
||||
<div class="margin-bottom">
|
||||
|
@ -40,9 +46,9 @@
|
|||
<div class="zone xl margin-bottom-no">
|
||||
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
|
||||
<li data-state="/console/database/document?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
|
||||
<h2>Overview</h2>
|
||||
<h2 class="margin-bottom">Overview</h2>
|
||||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="row responsive">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form
|
||||
data-analytics
|
||||
|
@ -54,28 +60,74 @@
|
|||
data-name="project-document"
|
||||
data-scope="sdk"
|
||||
data-event="submit"
|
||||
data-success="alert,trigger"
|
||||
data-success-param-alert-text="Updated document successfully"
|
||||
data-success="trigger,redirect"
|
||||
data-success-param-trigger-events="database.updateDocument"
|
||||
data-success-param-redirect-url="/console/database/document?id={{serviceData.$id}}&collection={{project-collection.$id}}&project={{router.params.project}}"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update document"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
|
||||
<input type="hidden" name="documentId" data-ls-bind="{{project-document.$id}}" />
|
||||
|
||||
<label> </label>
|
||||
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
|
||||
<?php if(!$new): ?><input type="hidden" name="documentId" data-ls-bind="{{project-document.$id}}" /><?php endif; ?>
|
||||
|
||||
<div class="box">
|
||||
<fieldset name="data" data-cast-to="object">
|
||||
<?php if($new): ?>
|
||||
<label for="documentId">Document ID</label>
|
||||
<input
|
||||
type="hidden"
|
||||
data-custom-id
|
||||
data-id-type="auto"
|
||||
data-validator="database.getDocument"
|
||||
required
|
||||
maxlength="36"
|
||||
name="documentId"
|
||||
id="documentId"
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$" />
|
||||
<?php endif; ?>
|
||||
|
||||
<fieldset name="data" data-cast-to="object">
|
||||
<ul data-ls-loop="project-collection.attributes" data-ls-as="attribute">
|
||||
<li>
|
||||
<li data-ls-if="{{attribute.status}} === 'available'">
|
||||
<label>
|
||||
<div data-ls-bind="{{attribute.key}}" class="margin-bottom-tiny"></div>
|
||||
<div data-ls-if="{{attribute.required}}" class="text-size-xs text-danger text-fade">required</div>
|
||||
<div data-ls-if="!{{attribute.required}}" class="text-size-xs text-fade">optional</div>
|
||||
</label>
|
||||
<textarea data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{project-document|documentAttribute}}"></textarea>
|
||||
<div data-ls-if="!{{attribute.array}}">
|
||||
<div data-ls-if="{{attribute.format}}" data-ls-template="template-{{attribute.format}}" data-type="script"></div>
|
||||
<div data-ls-if="!{{attribute.format}}" data-ls-template="template-{{attribute.type}}" data-type="script"></div>
|
||||
</div>
|
||||
|
||||
<div data-ls-if="{{attribute.array}}">
|
||||
<input type="hidden" data-forms-required="{{attribute.required}}" data-ls-attrs="name={{attribute.key}}" data-cast-to="array-empty">
|
||||
<div data-ls-loop="project-document.{{attribute.key}}" data-ls-as="item">
|
||||
<div class="row responsive thin margin-bottom-tiny" data-forms-remove>
|
||||
<div class="col span-11 margin-bottom-small">
|
||||
<div data-ls-if="{{attribute.format}}" data-ls-template="template-{{attribute.format}}-array" data-type="script"></div>
|
||||
<div data-ls-if="!{{attribute.format}}" data-ls-template="template-{{attribute.type}}-array" data-type="script"></div>
|
||||
</div>
|
||||
<div class="col span-1 margin-bottom-small">
|
||||
<button type="button" data-remove class="dark danger small round pull-end" style="margin-top: 10px;"><i class="icon-cancel"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-ls-attrs="id=attribute-{{attribute.key}}"></div>
|
||||
|
||||
<div class="margin-bottom">
|
||||
<div data-forms-clone data-label="Add Attribute" data-target="attribute-{{attribute.key}}" data-first="0">
|
||||
<div class="row responsive thin margin-bottom-tiny" data-forms-remove>
|
||||
<div class="col span-11 margin-bottom-small">
|
||||
<div data-ls-if="{{attribute.format}}" data-ls-template="template-{{attribute.format}}-array" data-type="script"></div>
|
||||
<div data-ls-if="!{{attribute.format}}" data-ls-template="template-{{attribute.type}}-array" data-type="script"></div>
|
||||
</div>
|
||||
<div class="col span-1 margin-bottom-small">
|
||||
<button type="button" data-remove class="dark danger small round pull-end" style="margin-top: 10px;"><i class="icon-cancel"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</fieldset>
|
||||
|
@ -145,10 +197,82 @@
|
|||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<!-- <li data-ls-if="{{project-document.$id}}" data-state="/console/database/document/activity?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
|
||||
<h2>Activity</h2>
|
||||
</li> -->
|
||||
<?php if(!$new): ?>
|
||||
<li data-state="/console/database/document/activity?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
|
||||
<h2>Activity <span class="badge" data-ls-bind="{{logs.sum}}"></span></h2>
|
||||
|
||||
<?php echo $logs->render(); ?>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/html" id="template-string">
|
||||
<textarea data-forms-text-resize data-forms-text-direction data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string"></textarea>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-string-array">
|
||||
<textarea data-forms-text-resize data-forms-text-direction data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string"></textarea>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-integer">
|
||||
<input type="number" step="1" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="integer" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-integer-array">
|
||||
<input type="number" step="1" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="integer" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-double">
|
||||
<input type="number" step="0.01" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="float" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-double-array">
|
||||
<input type="number" step="0.01" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="float" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-boolean">
|
||||
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="boolean" class="margin-bottom-no" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-boolean-array">
|
||||
<input type="hidden" data-forms-switch data-ls-attrs="name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="boolean" class="margin-bottom-no" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-url">
|
||||
<input type="url" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-url-array">
|
||||
<input type="url" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-email">
|
||||
<input type="email" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-email-array">
|
||||
<input type="email" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-ip">
|
||||
<input type="text" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-forms-required="{{attribute.required}}" data-ls-bind="{{project-document|documentAttribute}}" data-cast-to="string" pattern="((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))" title="Enter a valid IPV4 or IPV6 address" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-ip-array">
|
||||
<input type="text" data-ls-attrs="placeholder={{attribute.default}},name={{attribute.key}}" data-ls-bind="{{item}}" data-cast-to="string" pattern="((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))" title="Enter a valid IPV4 or IPV6 address" />
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-enum">
|
||||
<select data-ls-attrs="name={{attribute.key}}" data-ls-loop="attribute.elements" data-ls-as="element" data-cast-to="string">
|
||||
<option data-ls-attrs="value={{element}}" data-ls-bind="{{element}}" data-forms-selected="{{project-document|documentAttribute}}"></option>
|
||||
</select>
|
||||
</script>
|
||||
|
||||
<script type="text/html" id="template-enum-array">
|
||||
<select data-ls-attrs="name={{attribute.key}}" data-ls-loop="attribute.elements" data-ls-as="element" data-cast-to="string">
|
||||
<option data-ls-attrs="value={{element}}" data-ls-bind="{{element}}" data-forms-selected="{{item}}"></option>
|
||||
</select>
|
||||
</script>
|
|
@ -102,6 +102,7 @@
|
|||
data-validator="database.getCollection"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
name="collectionId" />
|
||||
|
||||
<label for="collection-name">Name</label>
|
||||
|
@ -118,9 +119,110 @@
|
|||
</div>
|
||||
|
||||
</li>
|
||||
<!-- <li data-state="/console/database/usage?project={{router.params.project}}">
|
||||
<li data-state="/console/database/usage?project={{router.params.project}}">
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="database.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="database.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="database.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
</li> -->
|
||||
|
||||
<div
|
||||
data-service="database.getUsage"
|
||||
data-event="load"
|
||||
data-name="usage">
|
||||
<h3 class="margin-bottom-tiny">Objects</h3>
|
||||
<p class="text-fade">Count of collections and documents over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Collections=collectionsCount,Documents=documentsCount"
|
||||
data-show-y-axis="true"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Total Collections <span data-ls-bind="({{usage.collectionsCount|statsGetLast|statsTotal}})"></span></li>
|
||||
<li>Total Documents <span data-ls-bind="({{usage.documentsCount|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 class="margin-bottom-tiny">Collections</h3>
|
||||
<p class="text-fade">Count of collections create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Created=collectionsCreate,Read=collectionsRead,Updated=collectionsUpdate,Deleted=collectionsDelete"
|
||||
data-show-y-axis="true"
|
||||
data-colors="create,read,update,delete"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large crud">
|
||||
<li>Created</li>
|
||||
<li>Read</li>
|
||||
<li>Updated</li>
|
||||
<li>Deleted</li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Documents</h3>
|
||||
<p class="text-fade">Count of documents create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Created=documentsCreate,Read=documentsRead,Updated=documentsUpdate,Deleted=documentsDelete"
|
||||
data-show-y-axis="true"
|
||||
data-colors="create,read,update,delete"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large crud">
|
||||
<li>Created</li>
|
||||
<li>Read</li>
|
||||
<li>Updated</li>
|
||||
<li>Deleted</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -247,37 +247,37 @@ $usageStatsEnabled = $this->getParam('usageStatsEnabled', true);
|
|||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Executions=usage.executions.data" data-height="140" />
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Executions=functionsExecutions" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Executions <span data-ls-bind="({{usage.executions.total}})"></span></li>
|
||||
<li>Executions <span data-ls-bind="({{usage.functionsExecutions|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="CPU Time (seconds)=usage.compute.data" data-colors="orange" data-height="140" />
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="CPU Time (seconds)=functionsCompute" data-colors="orange" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li class="orange">CPU Time <span data-ls-bind="({{usage.compute.total|seconds2hum}})"></span></li>
|
||||
<li class="orange">CPU Time <span data-ls-bind="({{usage.functionsCompute|statsGetLast|seconds2hum}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Failures=usage.failures.data" data-colors="red" data-height="140" />
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Failures=functionsFailures" data-colors="red" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li class="red">Errors <span data-ls-bind="({{usage.failures.total}})"></span></li>
|
||||
<li class="red">Errors <span data-ls-bind="({{usage.functionsFailures|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -113,6 +113,7 @@ $runtimes = $this->getParam('runtimes', []);
|
|||
data-validator="functions.get"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
name="functionId" />
|
||||
|
||||
<label for="name">Name</label>
|
||||
|
@ -125,7 +126,7 @@ $runtimes = $this->getParam('runtimes', []);
|
|||
<?php endforeach;?>
|
||||
</select>
|
||||
|
||||
<input id="execute" name="execute" value="" hidden/>
|
||||
<input id="execute" name="execute" value="" hidden />
|
||||
|
||||
<footer>
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
|
|
|
@ -39,7 +39,7 @@ $home = $this->getParam('home', '');
|
|||
<li class="margin-bottom">
|
||||
<a data-ls-attrs="href=/console/home?project={{project.$id}}" class="box">
|
||||
<div data-ls-bind="{{project.name}}" class="text-one-liner margin-bottom-tiny text-bold"> </div>
|
||||
|
||||
|
||||
<p data-ls-if="({{project.platforms.length}})" class="text-fade text-size-small" data-ls-bind="{{project.platforms.length}} apps"></p>
|
||||
<p data-ls-if="(!{{project.platforms.length}})" class="text-fade text-size-small"> </p>
|
||||
|
||||
|
@ -81,7 +81,7 @@ $home = $this->getParam('home', '');
|
|||
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{console-projects.sum}}" class="margin-start round small" aria-label="Next"><i class="icon-right-open"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<button data-ls-ui-trigger="create-project">Create Project</button>
|
||||
</div>
|
||||
|
||||
|
@ -89,9 +89,9 @@ $home = $this->getParam('home', '');
|
|||
<div class="col span-6 margin-bottom">
|
||||
<div class="box line community">
|
||||
<h3 class="margin-bottom-small text-size-normal">Join The Community</h3>
|
||||
|
||||
|
||||
<p class="text-fade">Join Appwrite growing developers community channels.</p>
|
||||
|
||||
|
||||
<a href="<?php echo APP_SOCIAL_TWITTER; ?>" target="_blank" rel="noopener" title="<?php echo APP_NAME;?> on Twitter"
|
||||
data-analytics
|
||||
data-analytics-event="click"
|
||||
|
@ -122,7 +122,7 @@ $home = $this->getParam('home', '');
|
|||
<div class="col span-6 margin-bottom">
|
||||
<div class="box line">
|
||||
<h3 class="margin-bottom-small text-size-normal">Read The Docs</h3>
|
||||
|
||||
|
||||
<p class="text-fade">Take full advantage of Appwrite APIs and tools for your new project.</p>
|
||||
|
||||
<a data-ls-attrs="href={{env.HOME}}/docs" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Full Documentation</a>
|
||||
|
|
|
@ -64,7 +64,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<!-- <div data-ls-if="0 !== {{console-domains|activeDomainsCount}}">
|
||||
<label for="name">Custom API Endpoints</label>
|
||||
|
||||
|
||||
<ul data-ls-loop="console-domains" data-ls-as="domain">
|
||||
<li>
|
||||
<div class="input-copy" data-ls-if="true === {{domain.verification}} && {{domain.certificateId}}">
|
||||
|
@ -73,7 +73,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</li>
|
||||
</ul>
|
||||
</div> -->
|
||||
|
||||
|
||||
|
||||
<button class="" type="submit">Update</button>
|
||||
</form>
|
||||
|
@ -110,14 +110,14 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<div data-ui-modal class="modal box close width-small height-small" data-button-text="Delete Project" data-button-class="danger reverse">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
|
||||
<h3>Confirm Password</h3>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<label>Password</label>
|
||||
<input name="password" type="password" class="full-width" autocomplete="off" placeholder="" required>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<button type="submit" class="margin-bottom-no danger fill">Delete Project</button>
|
||||
|
@ -232,7 +232,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</form>
|
||||
|
||||
<img src="<?php echo $this->escape($icon); ?>?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="" class="pull-start provider margin-end" />
|
||||
|
||||
|
||||
<span class="text-size-small text-bold"><?php echo $this->escape($name); ?></span>
|
||||
|
||||
<?php if($docs): ?>
|
||||
|
@ -241,7 +241,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="text-align-center text-size-small text-bold text-success" data-ls-if="!!({{console-project.serviceStatusFor<?php echo ucFirst($this->escape($key)); ?>}})">Enabled</div>
|
||||
|
@ -252,15 +252,15 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</ul>
|
||||
</li>
|
||||
<li data-state="/console/settings/domains?project={{router.params.project}}">
|
||||
|
||||
|
||||
<?php if(!$customDomainsEnabled): ?>
|
||||
<h2 style="display: none;">Custom Domains</h2>
|
||||
|
||||
<div class="box line margin-bottom">
|
||||
<h3>Enable Custom Domains</h3>
|
||||
|
||||
|
||||
<p>To enable <?php echo APP_NAME; ?>'s custom domain feature, you have to start your server instance with a public accessible domain name.</p>
|
||||
|
||||
|
||||
<p>Start your <?php echo APP_NAME; ?> server container with the <b>_APP_DOMAIN_TARGET</b> environment variable set with a public accessible domain name that resolves to your <?php echo APP_NAME; ?> server setup.</p>
|
||||
<p class="margin-bottom-no">The <?php echo APP_NAME; ?> server will use your target domain to validate new custom domains and will automatically generate SSL certificates for your new domains using Let'sencrypt Certbot.</p>
|
||||
</div>
|
||||
|
@ -312,7 +312,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<td data-title="">
|
||||
<button class="link text-size-small" data-ls-if="true === {{domain.verification}}" data-ls-ui-trigger="dns-settings-{{domain.$id}}">DNS Settings</button>
|
||||
<button class="link text-size-small" data-ls-if="true !== {{domain.verification}}" data-ls-ui-trigger="dns-settings-{{domain.$id}}">Verify Domain</button>
|
||||
|
||||
|
||||
<div data-ui-modal class="modal box close" data-button-alias="none" data-open-event="dns-settings-{{domain.$id}}" xdata-close-event="dns-settings-close-{{domain.$id}}">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
|
@ -326,7 +326,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<ol class="bullets">
|
||||
<li>
|
||||
<p>Add a new CNAME record in your DNS providers settings to point your new subdomain to your <?php echo APP_NAME; ?> server with the following value:</p>
|
||||
|
||||
|
||||
<div class="ide margin-bottom-small">
|
||||
<pre class="line-numbers"><code class="prism language-javascript" data-prism><?php echo $this->print($customDomainsTarget, 'escape'); ?></code></pre>
|
||||
</div>
|
||||
|
@ -359,7 +359,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
|
||||
<input type="hidden" name="domainId" data-ls-bind="{{domain.$id}}" />
|
||||
|
||||
|
||||
<button class="margin-top-small">Confirm & Verify</button>
|
||||
</form>
|
||||
</li>
|
||||
|
@ -428,7 +428,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<label for="name">Domain Name</label>
|
||||
<input type="text" class="full-width" id="domain" name="domain" placeholder="appwrite.example.com" required autocomplete="off" title="Enter a valid domain name" pattern="^([a-zA-Z0-9][a-zA-Z0-9-_]*\.)*[a-zA-Z0-9]*[a-zA-Z0-9-_]*[[a-zA-Z0-9]+$" />
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<button type="submit">Create</button> <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
|
||||
|
@ -463,6 +463,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-scope="console"
|
||||
data-event="submit"
|
||||
data-success="alert,trigger"
|
||||
data-confirm="Are you sure you want to remove that user from the team?"
|
||||
data-success-param-alert-text="Member Removed Successfully"
|
||||
data-success-param-trigger-events="teams.deleteMembership"
|
||||
data-failure="alert"
|
||||
|
@ -474,7 +475,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<button class="danger">Leave</button>
|
||||
</form>
|
||||
|
||||
|
||||
<div data-ls-if="false === {{member.confirm}}" class="pull-end margin-end">
|
||||
<form class="pull-end"
|
||||
data-analytics
|
||||
|
@ -553,7 +554,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<label for="email">Email</label>
|
||||
<input name="email" id="email" type="email" autocomplete="email" required>
|
||||
|
||||
|
||||
<label for="team-name">Name <small>(optional)</small></label>
|
||||
<input name="name" id="team-name" type="text" autocomplete="name" maxlength="128" />
|
||||
|
||||
|
|
|
@ -163,6 +163,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-validator="users.get"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
id="userId"
|
||||
name="userId" />
|
||||
|
||||
|
@ -310,6 +311,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-validator="teams.get"
|
||||
required
|
||||
maxlength="36"
|
||||
pattern="^[a-zA-Z0-9][a-zA-Z0-9_-]{1,36}$"
|
||||
id="teamId"
|
||||
name="teamId" />
|
||||
|
||||
|
@ -327,7 +329,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<li data-state="/console/users/providers?project={{router.params.project}}">
|
||||
<p data-ls-if="{{console-project.authLimit}} == 0" class="text-fade text-size-small margin-bottom pull-end">Unlimited Users <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Set Limit</a></p>
|
||||
<p data-ls-if="{{console-project.authLimit}} != 0" class="text-fade text-size-small margin-bottom pull-end"><span data-ls-bind="{{console-project.authLimit|statsTotal}}"></span> Users allowed <span class="link" data-ls-ui-trigger="project-update-auth-users-limit">Change Limit</a></p>
|
||||
|
||||
|
||||
<h2>Settings</h2>
|
||||
|
||||
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="project-update-auth-users-limit">
|
||||
|
@ -335,8 +337,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<h1>Max Allowed Users</h1>
|
||||
|
||||
<form data-debug="1"
|
||||
data-analytics
|
||||
<form data-analytics
|
||||
data-analytics-activity
|
||||
data-analytics-event="submit"
|
||||
data-analytics-category="console"
|
||||
|
@ -369,7 +370,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
|
||||
<ul class="tiles cell-3 margin-bottom-small">
|
||||
<?php foreach($auth as $index => $method):
|
||||
$key = $method['key'] ?? '';
|
||||
$key = ucfirst($method['key'] ?? '');
|
||||
$name = $method['name'] ?? '';
|
||||
$icon = $method['icon'] ?? '';
|
||||
$docs = $method['docs'] ?? '';
|
||||
|
@ -394,17 +395,17 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to update project auth status settings"
|
||||
data-failure-param-alert-classname="error">
|
||||
<input name="method" id="<?php echo $this->escape($key); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($index); ?>">
|
||||
<input name="method" id="auth<?php echo $this->escape($key); ?>" type="hidden" autocomplete="off" value="<?php echo $this->escape($index); ?>">
|
||||
<?php if( !(in_array($key, ['usersAuthMagicURL', 'usersAuthInvites']) && !$smtpEnabled)): ?>
|
||||
<input name="status" type="hidden" data-forms-switch data-ls-bind="{{console-project.<?php echo $this->escape($key); ?>}}" data-cast-to="boolean" class="pull-end" />
|
||||
<input name="status" type="hidden" data-forms-switch data-ls-bind="{{console-project.auth<?php echo $this->escape($key); ?>}}" data-cast-to="boolean" class="pull-end" />
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<img src="<?php echo $this->escape($icon); ?>?buster=<?php echo APP_CACHE_BUSTER; ?>" alt="Email/Password Logo" class="pull-start provider margin-end" />
|
||||
|
||||
|
||||
<span class="text-size-small"><?php echo $this->escape($name); ?><?php if(!$enabled): ?> <spann class="text-fade text-size-xs">soon</span><?php endif; ?>
|
||||
|
||||
|
||||
<?php if( in_array($key, ['usersAuthMagicURL', 'usersAuthInvites']) && !$smtpEnabled): ?>
|
||||
<p class="margin-bottom-no text-one-liner text-size-small text-danger">
|
||||
SMTP Disabled
|
||||
|
@ -431,18 +432,18 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
data-scope="console">
|
||||
<ul class="tiles cell-3 margin-bottom-small">
|
||||
<?php foreach ($providers as $provider => $data):
|
||||
if (isset($data['enabled']) && !$data['enabled']) {continue;}
|
||||
// if (isset($data['mock']) && $data['mock']) {continue;}
|
||||
if (isset($data['enabled']) && !$data['enabled']) {continue;}
|
||||
if (isset($data['mock']) && $data['mock']) {continue;}
|
||||
$sandbox = $data['sandbox'] ?? false;
|
||||
$form = $data['form'] ?? false;
|
||||
$name = $data['name'] ?? 'Unknown';
|
||||
$beta = $data['beta'] ?? false;
|
||||
?>
|
||||
<li class="<?php echo (isset($data['enabled']) && !$data['enabled']) ? 'dev-feature' : ''; ?>">
|
||||
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
?>
|
||||
<li class="<?php echo (isset($data['enabled']) && !$data['enabled']) ? 'dev-feature' : ''; ?>">
|
||||
<div data-ui-modal class="modal close" data-button-alias="none" data-open-event="provider-update-<?php echo $provider; ?>">
|
||||
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
|
||||
|
||||
<h1><?php echo $this->escape($name); ?> <?php if ($sandbox): ?>Sandbox<?php endif;?> OAuth2 Settings</h1>
|
||||
<h1><?php echo $this->escape($name); ?> <?php if ($sandbox): ?>Sandbox<?php endif;?> OAuth2 Settings</h1>
|
||||
|
||||
<form
|
||||
autocomplete="off"
|
||||
|
@ -470,7 +471,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
<?php if (!$form): ?>
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid">App ID</label>
|
||||
<input name="appId" id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Appid" type="text" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Appid}}">
|
||||
|
||||
|
||||
<label for="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret">App Secret</label>
|
||||
<input name="secret" data-forms-show-secret id="oauth2<?php echo $this->escape(ucfirst($provider)); ?>Secret" type="password" autocomplete="off" data-ls-bind="{{console-project.provider<?php echo $this->escape(ucfirst($provider)); ?>Secret}}">
|
||||
<?php else: ?>
|
||||
|
@ -524,5 +525,79 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
|
|||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li data-state="/console/users/usage?project={{router.params.project}}">
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '90d'"
|
||||
data-service="users.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="90d">
|
||||
<button class="tick">90d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '90d'" disabled>90d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '30d'"
|
||||
data-service="users.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage">
|
||||
<button class="tick">30d</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '30d'" disabled>30d</button>
|
||||
|
||||
<form
|
||||
class="pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} !== '24h'"
|
||||
data-service="users.getUsage"
|
||||
data-event="submit"
|
||||
data-name="usage"
|
||||
data-param-range="24h">
|
||||
<button class="tick">24h</button>
|
||||
</form>
|
||||
|
||||
<button class="tick pull-end margin-start-small margin-top-small" data-ls-if="{{usage.range}} === '24h'" disabled>24h</button>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<div data-service="users.getUsage" data-event="load" data-name="usage">
|
||||
<h3 class="margin-bottom-tiny">Users</h3>
|
||||
<p class="text-fade">Count of users over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input type="hidden" data-ls-bind="{{usage}}" data-forms-chart="Users=usersCount" data-show-y-axis="true" data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large">
|
||||
<li>Users <span data-ls-bind="({{usage.usersCount|statsGetLast|statsTotal}})"></span></li>
|
||||
</ul>
|
||||
|
||||
<h3 class="margin-bottom-tiny">Operations</h3>
|
||||
<p class="text-fade">Count of users create, read, update and delete operations over time</p>
|
||||
<div class="box margin-bottom-small">
|
||||
<div class="margin-start-negative-small margin-end-negative-small margin-top-negative-small margin-bottom-negative-small">
|
||||
<div class="chart margin-bottom-no">
|
||||
<input
|
||||
type="hidden"
|
||||
data-ls-bind="{{usage}}"
|
||||
data-forms-chart="Created=usersCreate,Read=usersRead,Updated=usersUpdate,Deleted=usersDelete"
|
||||
data-show-y-axis="true"
|
||||
data-colors="create,read,update,delete"
|
||||
data-height="140" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="chart-notes margin-bottom-large crud">
|
||||
<li>Created</li>
|
||||
<li>Read</li>
|
||||
<li>Updated</li>
|
||||
<li>Deleted</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -98,6 +98,7 @@
|
|||
data-service="teams.deleteMembership"
|
||||
data-event="submit"
|
||||
data-success="alert,trigger"
|
||||
data-confirm="Are you sure you want to remove that user from the team?"
|
||||
data-success-param-alert-text="Member Removed Successfully"
|
||||
data-success-param-trigger-events="teams.deleteMembership"
|
||||
data-failure="alert"
|
||||
|
|
|
@ -106,12 +106,18 @@ class DatabaseV1 extends Worker
|
|||
$dbForExternal = $this->getExternalDB($projectId);
|
||||
$collectionId = $collection->getId();
|
||||
$key = $attribute->getAttribute('key', '');
|
||||
$status = $attribute->getAttribute('status', '');
|
||||
|
||||
// possible states at this point:
|
||||
// - available: should not land in queue; controller flips these to 'deleting'
|
||||
// - processing: hasn't finished creating
|
||||
// - deleting: was available, in deletion queue for first time
|
||||
// - failed: attribute was never created
|
||||
// - stuck: attribute was available but cannot be removed
|
||||
try {
|
||||
if(!$dbForExternal->deleteAttribute($collectionId, $key) && $attribute->getAttribute('status') !== 'failed') {
|
||||
if($status !== 'failed' && !$dbForExternal->deleteAttribute($collectionId, $key)) {
|
||||
throw new Exception('Failed to delete Attribute');
|
||||
}
|
||||
|
||||
$dbForInternal->deleteDocument('attributes', $attribute->getId());
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
|
@ -214,12 +220,12 @@ class DatabaseV1 extends Worker
|
|||
|
||||
$collectionId = $collection->getId();
|
||||
$key = $index->getAttribute('key');
|
||||
$status = $index->getAttribute('status', '');
|
||||
|
||||
try {
|
||||
if(!$dbForExternal->deleteIndex($collectionId, $key) && $index->getAttribute('status') !== 'failed') {
|
||||
if($status !== 'failed' && !$dbForExternal->deleteIndex($collectionId, $key)) {
|
||||
throw new Exception('Failed to delete index');
|
||||
}
|
||||
|
||||
$dbForInternal->deleteDocument('indexes', $index->getId());
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
|
|
|
@ -15,7 +15,7 @@ use Utopia\Abuse\Adapters\TimeLimit;
|
|||
use Utopia\CLI\Console;
|
||||
use Utopia\Audit\Audit;
|
||||
|
||||
require_once __DIR__.'/../init.php';
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
Console::title('Deletes V1 Worker');
|
||||
Console::success(APP_NAME . ' deletes worker v1 has started' . "\n");
|
||||
|
@ -35,12 +35,11 @@ class DeletesV1 extends Worker
|
|||
{
|
||||
$projectId = $this->args['projectId'] ?? '';
|
||||
$type = $this->args['type'] ?? '';
|
||||
|
||||
|
||||
switch (strval($type)) {
|
||||
case DELETE_TYPE_DOCUMENT:
|
||||
$document = $this->args['document'] ?? [];
|
||||
$document = new Document($document);
|
||||
|
||||
$document = new Document($this->args['document'] ?? []);
|
||||
|
||||
switch ($document->getCollection()) {
|
||||
case DELETE_TYPE_COLLECTIONS:
|
||||
$this->deleteCollection($document, $projectId);
|
||||
|
@ -79,8 +78,7 @@ class DeletesV1 extends Worker
|
|||
break;
|
||||
|
||||
case DELETE_TYPE_REALTIME:
|
||||
//$this->deleteRealtimeUsage($this->args['timestamp']);
|
||||
//TODO: implement this
|
||||
$this->deleteRealtimeUsage($this->args['timestamp']);
|
||||
break;
|
||||
|
||||
case DELETE_TYPE_CERTIFICATES:
|
||||
|
@ -127,12 +125,10 @@ class DeletesV1 extends Worker
|
|||
* @param int $timestamp1d
|
||||
* @param int $timestamp30m
|
||||
*/
|
||||
protected function deleteUsageStats(int $timestamp1d, int $timestamp30m) {
|
||||
$this->deleteForProjectIds(function($projectId) use ($timestamp1d, $timestamp30m) {
|
||||
if (!($dbForInternal = $this->getInternalDB($projectId))) {
|
||||
throw new Exception('Failed to get projectDB for project '.$projectId);
|
||||
}
|
||||
|
||||
protected function deleteUsageStats(int $timestamp1d, int $timestamp30m)
|
||||
{
|
||||
$this->deleteForProjectIds(function (string $projectId) use ($timestamp1d, $timestamp30m) {
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
// Delete Usage stats
|
||||
$this->deleteByGroup('stats', [
|
||||
new Query('time', Query::TYPE_LESSER, [$timestamp1d]),
|
||||
|
@ -145,7 +141,7 @@ class DeletesV1 extends Worker
|
|||
], $dbForInternal);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Document $document teams document
|
||||
* @param string $projectId
|
||||
|
@ -166,13 +162,14 @@ class DeletesV1 extends Worker
|
|||
protected function deleteProject(Document $document): void
|
||||
{
|
||||
$projectId = $document->getId();
|
||||
|
||||
// Delete all DBs
|
||||
$this->getExternalDB($projectId)->delete();
|
||||
$this->getInternalDB($projectId)->delete();
|
||||
|
||||
// Delete all storage directories
|
||||
$uploads = new Local(APP_STORAGE_UPLOADS.'/app-'.$document->getId());
|
||||
$cache = new Local(APP_STORAGE_CACHE.'/app-'.$document->getId());
|
||||
$uploads = new Local(APP_STORAGE_UPLOADS . '/app-' . $document->getId());
|
||||
$cache = new Local(APP_STORAGE_CACHE . '/app-' . $document->getId());
|
||||
|
||||
$uploads->delete($uploads->getRoot(), true);
|
||||
$cache->delete($cache->getRoot(), true);
|
||||
|
@ -190,12 +187,12 @@ class DeletesV1 extends Worker
|
|||
// Delete Memberships and decrement team membership counts
|
||||
$this->deleteByGroup('memberships', [
|
||||
new Query('userId', Query::TYPE_EQUAL, [$userId])
|
||||
], $this->getInternalDB($projectId), function(Document $document) use ($projectId, $userId) {
|
||||
], $this->getInternalDB($projectId), function (Document $document) use ($projectId) {
|
||||
|
||||
if ($document->getAttribute('confirm')) { // Count only confirmed members
|
||||
$teamId = $document->getAttribute('teamId');
|
||||
$team = $this->getInternalDB($projectId)->getDocument('teams', $teamId);
|
||||
if(!$team->isEmpty()) {
|
||||
if (!$team->isEmpty()) {
|
||||
$team = $this->getInternalDB($projectId)->updateDocument('teams', $teamId, new Document(\array_merge($team->getArrayCopy(), [
|
||||
'sum' => \max($team->getAttribute('sum', 0) - 1, 0), // Ensure that sum >= 0
|
||||
])));
|
||||
|
@ -209,11 +206,8 @@ class DeletesV1 extends Worker
|
|||
*/
|
||||
protected function deleteExecutionLogs(int $timestamp): void
|
||||
{
|
||||
$this->deleteForProjectIds(function(string $projectId) use ($timestamp) {
|
||||
if (!($dbForInternal = $this->getInternalDB($projectId))) {
|
||||
throw new Exception('Failed to get projectDB for project '.$projectId);
|
||||
}
|
||||
|
||||
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
// Delete Executions
|
||||
$this->deleteByGroup('executions', [
|
||||
new Query('dateCreated', Query::TYPE_LESSER, [$timestamp])
|
||||
|
@ -221,18 +215,33 @@ class DeletesV1 extends Worker
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $timestamp
|
||||
*/
|
||||
protected function deleteRealtimeUsage(int $timestamp): void
|
||||
{
|
||||
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
// Delete Dead Realtime Logs
|
||||
$this->deleteByGroup('realtime', [
|
||||
new Query('timestamp', Query::TYPE_LESSER, [$timestamp])
|
||||
], $dbForInternal);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $timestamp
|
||||
*/
|
||||
protected function deleteAbuseLogs(int $timestamp): void
|
||||
{
|
||||
if($timestamp == 0) {
|
||||
if ($timestamp == 0) {
|
||||
throw new Exception('Failed to delete audit logs. No timestamp provided');
|
||||
}
|
||||
|
||||
$this->deleteForProjectIds(function($projectId) use ($timestamp){
|
||||
$timeLimit = new TimeLimit("", 0, 1, $this->getInternalDB($projectId));
|
||||
$abuse = new Abuse($timeLimit);
|
||||
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$timeLimit = new TimeLimit("", 0, 1, $dbForInternal);
|
||||
$abuse = new Abuse($timeLimit);
|
||||
|
||||
$status = $abuse->cleanup($timestamp);
|
||||
if (!$status) {
|
||||
|
@ -246,11 +255,12 @@ class DeletesV1 extends Worker
|
|||
*/
|
||||
protected function deleteAuditLogs(int $timestamp): void
|
||||
{
|
||||
if($timestamp == 0) {
|
||||
if ($timestamp == 0) {
|
||||
throw new Exception('Failed to delete audit logs. No timestamp provided');
|
||||
}
|
||||
$this->deleteForProjectIds(function($projectId) use ($timestamp){
|
||||
$audit = new Audit($this->getInternalDB($projectId));
|
||||
$this->deleteForProjectIds(function (string $projectId) use ($timestamp) {
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$audit = new Audit($dbForInternal);
|
||||
$status = $audit->cleanup($timestamp);
|
||||
if (!$status) {
|
||||
throw new Exception('Failed to delete Audit logs for project' . $projectId);
|
||||
|
@ -265,12 +275,12 @@ class DeletesV1 extends Worker
|
|||
protected function deleteFunction(Document $document, string $projectId): void
|
||||
{
|
||||
$dbForInternal = $this->getInternalDB($projectId);
|
||||
$device = new Local(APP_STORAGE_FUNCTIONS.'/app-'.$projectId);
|
||||
$device = new Local(APP_STORAGE_FUNCTIONS . '/app-' . $projectId);
|
||||
|
||||
// Delete Tags
|
||||
$this->deleteByGroup('tags', [
|
||||
new Query('functionId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $dbForInternal, function(Document $document) use ($device) {
|
||||
], $dbForInternal, function (Document $document) use ($device) {
|
||||
|
||||
if ($device->delete($document->getAttribute('path', ''))) {
|
||||
Console::success('Delete code tag: ' . $document->getAttribute('path', ''));
|
||||
|
@ -297,8 +307,8 @@ class DeletesV1 extends Worker
|
|||
{
|
||||
Authorization::disable();
|
||||
|
||||
if($database->deleteDocument($document->getCollection(), $document->getId())) {
|
||||
Console::success('Deleted document "'.$document->getId().'" successfully');
|
||||
if ($database->deleteDocument($document->getCollection(), $document->getId())) {
|
||||
Console::success('Deleted document "' . $document->getId() . '" successfully');
|
||||
|
||||
if (is_callable($callback)) {
|
||||
$callback($document);
|
||||
|
@ -326,16 +336,15 @@ class DeletesV1 extends Worker
|
|||
|
||||
$executionStart = \microtime(true);
|
||||
|
||||
while($sum === $limit) {
|
||||
while ($sum === $limit) {
|
||||
Authorization::disable();
|
||||
$projects = $this->getConsoleDB()->find('projects', [], $limit, ($chunk * $limit));
|
||||
Authorization::reset();
|
||||
|
||||
$chunk++;
|
||||
|
||||
$projectIds = array_map (function ($project) {
|
||||
return $project->getId();
|
||||
}, $projects);
|
||||
/** @var string[] $projectIds */
|
||||
$projectIds = array_map(fn(Document $project) => $project->getId(), $projects);
|
||||
|
||||
$sum = count($projects);
|
||||
|
||||
|
@ -366,7 +375,7 @@ class DeletesV1 extends Worker
|
|||
|
||||
$executionStart = \microtime(true);
|
||||
|
||||
while($sum === $limit) {
|
||||
while ($sum === $limit) {
|
||||
$chunk++;
|
||||
|
||||
Authorization::disable();
|
||||
|
|
|
@ -41,11 +41,11 @@
|
|||
"utopia-php/framework": "0.19.*",
|
||||
"utopia-php/abuse": "0.6.*",
|
||||
"utopia-php/analytics": "0.2.*",
|
||||
"utopia-php/audit": "0.6.*",
|
||||
"utopia-php/audit": "0.7.*",
|
||||
"utopia-php/cache": "0.4.*",
|
||||
"utopia-php/cli": "0.11.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.10.*",
|
||||
"utopia-php/database": "0.12.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/orchestration": "0.2.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
|
|
22
composer.lock
generated
22
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d2f722e3c9fb35f72fba310543755c76",
|
||||
"content-hash": "d49875c416f7ee3ef27bbc3d5de26fe9",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -1928,22 +1928,22 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/audit",
|
||||
"version": "0.6.3",
|
||||
"version": "0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/audit.git",
|
||||
"reference": "d79b467fbc7d03e5e02f12cdeb08761507a60ca0"
|
||||
"reference": "485cdd2354db7eb8f7aa74bbe39c39b583e99c04"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/d79b467fbc7d03e5e02f12cdeb08761507a60ca0",
|
||||
"reference": "d79b467fbc7d03e5e02f12cdeb08761507a60ca0",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/485cdd2354db7eb8f7aa74bbe39c39b583e99c04",
|
||||
"reference": "485cdd2354db7eb8f7aa74bbe39c39b583e99c04",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=7.4",
|
||||
"utopia-php/database": ">=0.6 <1.0"
|
||||
"utopia-php/database": ">=0.11 <1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
|
@ -1975,9 +1975,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/audit/issues",
|
||||
"source": "https://github.com/utopia-php/audit/tree/0.6.3"
|
||||
"source": "https://github.com/utopia-php/audit/tree/0.7.0"
|
||||
},
|
||||
"time": "2021-08-16T18:49:55+00:00"
|
||||
"time": "2021-11-17T17:23:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cache",
|
||||
|
@ -2138,11 +2138,11 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.10.1",
|
||||
"version": "0.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database",
|
||||
"reference": "9b4697612a2cd1ad55beeb6a02570f6ffe26dc1e"
|
||||
"reference": "102ee1d21fd55fc92dc7a07b60672a98ae49be26"
|
||||
},
|
||||
"require": {
|
||||
"ext-mongodb": "*",
|
||||
|
@ -2191,7 +2191,7 @@
|
|||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"time": "2021-11-02T15:10:39+00:00"
|
||||
"time": "2021-11-24T14:53:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
|
1
docs/references/database/get-document-logs.md
Normal file
1
docs/references/database/get-document-logs.md
Normal file
|
@ -0,0 +1 @@
|
|||
Get the document activity logs list by its unique ID.
|
|
@ -58,14 +58,16 @@ For **Windows** add your app <u>name</u> and <u>package name</u>, Your package n
|
|||
|
||||
```dart
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
Client client = Client();
|
||||
|
||||
void main() {
|
||||
Client client = Client();
|
||||
|
||||
client
|
||||
.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint
|
||||
.setProject('5e8cf4f46b5e8') // Your project ID
|
||||
.setSelfSigned() // Use only on dev mode with a self-signed SSL cert
|
||||
;
|
||||
client
|
||||
.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint
|
||||
.setProject('5e8cf4f46b5e8') // Your project ID
|
||||
.setSelfSigned() // Use only on dev mode with a self-signed SSL cert
|
||||
;
|
||||
}
|
||||
```
|
||||
|
||||
Before starting to send any API calls to your new Appwrite instance, make sure your Android or iOS emulators has network access to the Appwrite server hostname or IP address.
|
||||
|
@ -91,25 +93,28 @@ Response user = await account
|
|||
|
||||
```dart
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
Client client = Client();
|
||||
|
||||
void main() {
|
||||
Client client = Client();
|
||||
|
||||
|
||||
client
|
||||
.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint
|
||||
.setProject('5e8cf4f46b5e8') // Your project ID
|
||||
.setSelfSigned() // Use only on dev mode with a self-signed SSL cert
|
||||
;
|
||||
client
|
||||
.setEndpoint('https://localhost/v1') // Your Appwrite Endpoint
|
||||
.setProject('5e8cf4f46b5e8') // Your project ID
|
||||
.setSelfSigned() // Use only on dev mode with a self-signed SSL cert
|
||||
;
|
||||
|
||||
|
||||
// Register User
|
||||
Account account = Account(client);
|
||||
// Register User
|
||||
Account account = Account(client);
|
||||
|
||||
Response user = await account
|
||||
.create(
|
||||
email: 'me@appwrite.io',
|
||||
password: 'password',
|
||||
name: 'My Name'
|
||||
);
|
||||
Response user = await account
|
||||
.create(
|
||||
email: 'me@appwrite.io',
|
||||
password: 'password',
|
||||
name: 'My Name'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
|
|
@ -13,6 +13,7 @@ const configApp = {
|
|||
mainFile: 'app.js',
|
||||
src: [
|
||||
'public/scripts/dependencies/litespeed.js',
|
||||
'public/scripts/dependencies/alpine.js',
|
||||
|
||||
'public/scripts/init.js',
|
||||
|
||||
|
@ -59,9 +60,11 @@ const configApp = {
|
|||
'public/scripts/views/forms/oauth-apple.js',
|
||||
'public/scripts/views/forms/password-meter.js',
|
||||
'public/scripts/views/forms/pell.js',
|
||||
'public/scripts/views/forms/required.js',
|
||||
'public/scripts/views/forms/remove.js',
|
||||
'public/scripts/views/forms/run.js',
|
||||
'public/scripts/views/forms/select-all.js',
|
||||
'public/scripts/views/forms/selected.js',
|
||||
'public/scripts/views/forms/show-secret.js',
|
||||
'public/scripts/views/forms/switch.js',
|
||||
'public/scripts/views/forms/tags.js',
|
||||
|
|
406
public/dist/scripts/app-all.js
vendored
406
public/dist/scripts/app-all.js
vendored
File diff suppressed because one or more lines are too long
22
public/dist/scripts/app-dep.js
vendored
22
public/dist/scripts/app-dep.js
vendored
|
@ -23,7 +23,9 @@ const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{
|
|||
if(typeof password==='undefined'){throw new AppwriteException('Missing required parameter: "password"');}
|
||||
let path='/account/email';let payload={};if(typeof email!=='undefined'){payload['email']=email;}
|
||||
if(typeof password!=='undefined'){payload['password']=password;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),createJWT:()=>__awaiter(this,void 0,void 0,function*(){let path='/account/jwt';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getLogs:()=>__awaiter(this,void 0,void 0,function*(){let path='/account/logs';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(name)=>__awaiter(this,void 0,void 0,function*(){if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),createJWT:()=>__awaiter(this,void 0,void 0,function*(){let path='/account/jwt';let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getLogs:(limit,offset)=>__awaiter(this,void 0,void 0,function*(){let path='/account/logs';let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(name)=>__awaiter(this,void 0,void 0,function*(){if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
let path='/account/name';let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),updatePassword:(password,oldPassword)=>__awaiter(this,void 0,void 0,function*(){if(typeof password==='undefined'){throw new AppwriteException('Missing required parameter: "password"');}
|
||||
let path='/account/password';let payload={};if(typeof password!=='undefined'){payload['password']=password;}
|
||||
|
@ -223,7 +225,11 @@ if(typeof read!=='undefined'){payload['read']=read;}
|
|||
if(typeof write!=='undefined'){payload['write']=write;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),deleteDocument:(collectionId,documentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/database/collections/{collectionId}/documents/{documentId}'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listIndexes:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/documents/{documentId}'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listDocumentLogs:(collectionId,documentId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
|
||||
let path='/database/collections/{collectionId}/documents/{documentId}/logs'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),listIndexes:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createIndex:(collectionId,indexId,type,attributes,orders)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
|
||||
if(typeof type==='undefined'){throw new AppwriteException('Missing required parameter: "type"');}
|
||||
|
@ -236,8 +242,10 @@ const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{
|
|||
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
|
||||
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
|
||||
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listCollectionLogs:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/logs'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(range)=>__awaiter(this,void 0,void 0,function*(){let path='/database/usage';let payload={};if(typeof range!=='undefined'){payload['range']=range;}
|
||||
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listCollectionLogs:(collectionId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/collections/{collectionId}/logs'.replace('{collectionId}',collectionId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getUsage:(range)=>__awaiter(this,void 0,void 0,function*(){let path='/database/usage';let payload={};if(typeof range!=='undefined'){payload['range']=range;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getCollectionUsage:(collectionId,range)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
|
||||
let path='/database/{collectionId}/usage'.replace('{collectionId}',collectionId);let payload={};if(typeof range!=='undefined'){payload['range']=range;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
|
||||
|
@ -574,8 +582,10 @@ let path='/users/{userId}'.replace('{userId}',userId);let payload={};const uri=n
|
|||
let path='/users/{userId}'.replace('{userId}',userId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),updateEmail:(userId,email)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
if(typeof email==='undefined'){throw new AppwriteException('Missing required parameter: "email"');}
|
||||
let path='/users/{userId}/email'.replace('{userId}',userId);let payload={};if(typeof email!=='undefined'){payload['email']=email;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),getLogs:(userId)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
let path='/users/{userId}/logs'.replace('{userId}',userId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(userId,name)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),getLogs:(userId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
let path='/users/{userId}/logs'.replace('{userId}',userId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
|
||||
if(typeof offset!=='undefined'){payload['offset']=offset;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateName:(userId,name)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
|
||||
let path='/users/{userId}/name'.replace('{userId}',userId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
|
||||
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),updatePassword:(userId,password)=>__awaiter(this,void 0,void 0,function*(){if(typeof userId==='undefined'){throw new AppwriteException('Missing required parameter: "userId"');}
|
||||
|
|
384
public/dist/scripts/app.js
vendored
384
public/dist/scripts/app.js
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-ltr.css
vendored
2
public/dist/styles/default-ltr.css
vendored
File diff suppressed because one or more lines are too long
2
public/dist/styles/default-rtl.css
vendored
2
public/dist/styles/default-rtl.css
vendored
File diff suppressed because one or more lines are too long
2697
public/scripts/dependencies/alpine.js
Normal file
2697
public/scripts/dependencies/alpine.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -321,12 +321,20 @@
|
|||
* Get currently logged in user list of latest security activity logs. Each
|
||||
* log returns user IP address, location and date and time of log.
|
||||
*
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getLogs: () => __awaiter(this, void 0, void 0, function* () {
|
||||
getLogs: (limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
let path = '/account/logs';
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
|
@ -1910,6 +1918,38 @@
|
|||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* List Document Logs
|
||||
*
|
||||
* Get the document activity logs list by its unique ID.
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {string} documentId
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
listDocumentLogs: (collectionId, documentId, limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
if (typeof documentId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "documentId"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/documents/{documentId}/logs'.replace('{collectionId}', collectionId).replace('{documentId}', documentId);
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
}, payload);
|
||||
}),
|
||||
/**
|
||||
* List Indexes
|
||||
*
|
||||
|
@ -2025,15 +2065,23 @@
|
|||
* Get the collection activity logs list by its unique ID.
|
||||
*
|
||||
* @param {string} collectionId
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
listCollectionLogs: (collectionId) => __awaiter(this, void 0, void 0, function* () {
|
||||
listCollectionLogs: (collectionId, limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof collectionId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "collectionId"');
|
||||
}
|
||||
let path = '/database/collections/{collectionId}/logs'.replace('{collectionId}', collectionId);
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
|
@ -4935,15 +4983,23 @@
|
|||
* Get the user activity logs list by its unique ID.
|
||||
*
|
||||
* @param {string} userId
|
||||
* @param {number} limit
|
||||
* @param {number} offset
|
||||
* @throws {AppwriteException}
|
||||
* @returns {Promise}
|
||||
*/
|
||||
getLogs: (userId) => __awaiter(this, void 0, void 0, function* () {
|
||||
getLogs: (userId, limit, offset) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (typeof userId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "userId"');
|
||||
}
|
||||
let path = '/users/{userId}/logs'.replace('{userId}', userId);
|
||||
let payload = {};
|
||||
if (typeof limit !== 'undefined') {
|
||||
payload['limit'] = limit;
|
||||
}
|
||||
if (typeof offset !== 'undefined') {
|
||||
payload['offset'] = offset;
|
||||
}
|
||||
const uri = new URL(this.config.endpoint + path);
|
||||
return yield this.call('get', uri, {
|
||||
'content-type': 'application/json',
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
window.ls.filter
|
||||
.add("avatar", function($value, element) {
|
||||
.add("avatar", function ($value, element) {
|
||||
if (!$value) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let size = element.dataset["size"] || 80;
|
||||
let name = $value.name || $value || "";
|
||||
|
||||
|
||||
name = (typeof name !== 'string') ? '--' : name;
|
||||
|
||||
return def =
|
||||
"/v1/avatars/initials?project=console"+
|
||||
"/v1/avatars/initials?project=console" +
|
||||
"&name=" +
|
||||
encodeURIComponent(name) +
|
||||
"&width=" +
|
||||
|
@ -18,26 +18,26 @@ window.ls.filter
|
|||
"&height=" +
|
||||
size;
|
||||
})
|
||||
.add("selectedCollection", function($value, router) {
|
||||
.add("selectedCollection", function ($value, router) {
|
||||
return $value === router.params.collectionId ? "selected" : "";
|
||||
})
|
||||
.add("selectedDocument", function($value, router) {
|
||||
.add("selectedDocument", function ($value, router) {
|
||||
return $value === router.params.documentId ? "selected" : "";
|
||||
})
|
||||
.add("localeString", function($value) {
|
||||
.add("localeString", function ($value) {
|
||||
$value = parseInt($value);
|
||||
return !Number.isNaN($value) ? $value.toLocaleString() : "";
|
||||
})
|
||||
.add("date", function($value, date) {
|
||||
.add("date", function ($value, date) {
|
||||
return date.format("Y-m-d", $value);
|
||||
})
|
||||
.add("dateTime", function($value, date) {
|
||||
.add("dateTime", function ($value, date) {
|
||||
return date.format("Y-m-d H:i", $value);
|
||||
})
|
||||
.add("dateText", function($value, date) {
|
||||
.add("dateText", function ($value, date) {
|
||||
return date.format("d M Y", $value);
|
||||
})
|
||||
.add("timeSince", function($value) {
|
||||
.add("timeSince", function ($value) {
|
||||
$value = $value * 1000;
|
||||
|
||||
let seconds = Math.floor((Date.now() - $value) / 1000);
|
||||
|
@ -50,7 +50,7 @@ window.ls.filter
|
|||
}
|
||||
|
||||
let value = seconds;
|
||||
|
||||
|
||||
if (seconds >= 31536000) {
|
||||
value = Math.floor(seconds / 31536000);
|
||||
unit = "year";
|
||||
|
@ -71,10 +71,10 @@ window.ls.filter
|
|||
if (value != 1) {
|
||||
unit = unit + "s";
|
||||
}
|
||||
|
||||
|
||||
return value + " " + unit + " " + direction;
|
||||
})
|
||||
.add("ms2hum", function($value) {
|
||||
.add("ms2hum", function ($value) {
|
||||
let temp = $value;
|
||||
const years = Math.floor(temp / 31536000),
|
||||
days = Math.floor((temp %= 31536000) / 86400),
|
||||
|
@ -95,37 +95,37 @@ window.ls.filter
|
|||
|
||||
return "< 1s";
|
||||
})
|
||||
.add("seconds2hum", function($value) {
|
||||
.add("seconds2hum", function ($value) {
|
||||
|
||||
var seconds = ($value).toFixed(3);
|
||||
var seconds = ($value).toFixed(3);
|
||||
|
||||
var minutes = ($value / (60)).toFixed(1);
|
||||
var minutes = ($value / (60)).toFixed(1);
|
||||
|
||||
var hours = ($value / (60 * 60)).toFixed(1);
|
||||
var hours = ($value / (60 * 60)).toFixed(1);
|
||||
|
||||
var days = ($value / (60 * 60 * 24)).toFixed(1);
|
||||
var days = ($value / (60 * 60 * 24)).toFixed(1);
|
||||
|
||||
if (seconds < 60) {
|
||||
return seconds + "s";
|
||||
} else if (minutes < 60) {
|
||||
return minutes + "m";
|
||||
} else if (hours < 24) {
|
||||
return hours + "h";
|
||||
} else {
|
||||
return days + "d"
|
||||
}
|
||||
if (seconds < 60) {
|
||||
return seconds + "s";
|
||||
} else if (minutes < 60) {
|
||||
return minutes + "m";
|
||||
} else if (hours < 24) {
|
||||
return hours + "h";
|
||||
} else {
|
||||
return days + "d"
|
||||
}
|
||||
})
|
||||
.add("markdown", function($value, markdown) {
|
||||
.add("markdown", function ($value, markdown) {
|
||||
return markdown.render($value);
|
||||
})
|
||||
.add("pageCurrent", function($value, env) {
|
||||
.add("pageCurrent", function ($value, env) {
|
||||
return Math.ceil(parseInt($value || 0) / env.PAGING_LIMIT) + 1;
|
||||
})
|
||||
.add("pageTotal", function($value, env) {
|
||||
.add("pageTotal", function ($value, env) {
|
||||
let total = Math.ceil(parseInt($value || 0) / env.PAGING_LIMIT);
|
||||
return total ? total : 1;
|
||||
})
|
||||
.add("humanFileSize", function($value) {
|
||||
.add("humanFileSize", function ($value) {
|
||||
if (!$value) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ window.ls.filter
|
|||
|
||||
return $value.toFixed(1);
|
||||
})
|
||||
.add("humanFileUnit", function($value) {
|
||||
.add("humanFileUnit", function ($value) {
|
||||
if (!$value) {
|
||||
return '';
|
||||
}
|
||||
|
@ -183,95 +183,102 @@ window.ls.filter
|
|||
|
||||
return $value[$value.length - 1].value;
|
||||
})
|
||||
.add("isEmpty", function($value) {
|
||||
.add("statsGetLast", function ($value) {
|
||||
if (!$value || $value.length < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $value[$value.length - 1].value;
|
||||
})
|
||||
.add("isEmpty", function ($value) {
|
||||
return (!!$value);
|
||||
})
|
||||
.add("isEmptyObject", function($value) {
|
||||
.add("isEmptyObject", function ($value) {
|
||||
return ((Object.keys($value).length === 0 && $value.constructor === Object) || $value.length === 0)
|
||||
})
|
||||
.add("activeDomainsCount", function($value) {
|
||||
.add("activeDomainsCount", function ($value) {
|
||||
let result = [];
|
||||
|
||||
if(Array.isArray($value)) {
|
||||
result = $value.filter(function(node) {
|
||||
|
||||
if (Array.isArray($value)) {
|
||||
result = $value.filter(function (node) {
|
||||
return (node.verification && node.certificateId);
|
||||
});
|
||||
}
|
||||
|
||||
return result.length;
|
||||
})
|
||||
.add("documentAction", function(container) {
|
||||
.add("documentAction", function (container) {
|
||||
let collection = container.get('project-collection');
|
||||
let document = container.get('project-document');
|
||||
|
||||
if(collection && document && !document.$id) {
|
||||
if (collection && document && !document.$id) {
|
||||
return 'database.createDocument';
|
||||
}
|
||||
|
||||
return 'database.updateDocument';
|
||||
})
|
||||
.add("documentSuccess", function(container) {
|
||||
.add("documentSuccess", function (container) {
|
||||
let document = container.get('project-document');
|
||||
|
||||
if(document && !document.$id) {
|
||||
if (document && !document.$id) {
|
||||
return ',redirect';
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("firstElement", function($value) {
|
||||
if($value && $value[0]) {
|
||||
.add("firstElement", function ($value) {
|
||||
if ($value && $value[0]) {
|
||||
return $value[0];
|
||||
}
|
||||
|
||||
return $value;
|
||||
})
|
||||
.add("platformsLimit", function($value) {
|
||||
.add("platformsLimit", function ($value) {
|
||||
return $value;
|
||||
})
|
||||
.add("limit", function($value) {
|
||||
.add("limit", function ($value) {
|
||||
let postfix = ($value.length >= 50) ? '...' : '';
|
||||
return $value.substring(0, 50) + postfix;
|
||||
;
|
||||
})
|
||||
.add("arraySentence", function($value) {
|
||||
if(!Array.isArray($value)) {
|
||||
.add("arraySentence", function ($value) {
|
||||
if (!Array.isArray($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value.join(", ").replace(/,\s([^,]+)$/, ' and $1');
|
||||
})
|
||||
.add("runtimeName", function($value, env) {
|
||||
if(env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
.add("runtimeName", function ($value, env) {
|
||||
if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
return env.RUNTIMES[$value].name;
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("runtimeLogo", function($value, env) {
|
||||
if(env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
.add("runtimeLogo", function ($value, env) {
|
||||
if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
return env.RUNTIMES[$value].logo;
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("runtimeVersion", function($value, env) {
|
||||
if(env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
.add("runtimeVersion", function ($value, env) {
|
||||
if (env && env.RUNTIMES && env.RUNTIMES[$value]) {
|
||||
return env.RUNTIMES[$value].version;
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.add("indexAttributes", function($value) {
|
||||
.add("indexAttributes", function ($value) {
|
||||
let output = '';
|
||||
|
||||
for(let i = 0; i < $value.attributes.length; i++) {
|
||||
for (let i = 0; i < $value.attributes.length; i++) {
|
||||
output += $value.attributes[i] + ' (' + $value.orders[i] + '), '
|
||||
}
|
||||
return output.slice(0, -2);
|
||||
})
|
||||
.add("collectionAttributes", function($value) {
|
||||
if(!Array.isArray($value)) {
|
||||
.add("collectionAttributes", function ($value) {
|
||||
if (!Array.isArray($value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
@ -281,17 +288,23 @@ window.ls.filter
|
|||
|
||||
return $value;
|
||||
})
|
||||
.add("documentAttribute", function($value, attribute) {
|
||||
if($value[attribute.key]) {
|
||||
.add("documentAttribute", function ($value, attribute) {
|
||||
if (attribute.key in $value) {
|
||||
return $value[attribute.key];
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
})
|
||||
.add("accessProject", function($value, router) {
|
||||
.add("accessProject", function ($value, router) {
|
||||
return ($value && $value.hasOwnProperty(router.params.project)) ? $value[router.params.project] : 0;
|
||||
})
|
||||
;
|
||||
.add("first", function ($value) {
|
||||
return $value[0].$id;
|
||||
})
|
||||
.add("last", function ($value) {
|
||||
return $value[$value.length - 1].$id;
|
||||
})
|
||||
;
|
||||
|
||||
function abbreviate(number, maxPlaces, forcePlaces, forceLetter) {
|
||||
number = Number(number);
|
||||
|
|
|
@ -123,3 +123,66 @@ window.addEventListener("load", async () => {
|
|||
}
|
||||
});
|
||||
|
||||
window.formValidation = (form, fields) => {
|
||||
const elements = form.elements;
|
||||
const actionHandler = (action, attribute) => {
|
||||
switch (action) {
|
||||
case "disable":
|
||||
elements[attribute].setAttribute("disabled", true);
|
||||
elements[attribute].dispatchEvent(new Event('change'));
|
||||
break;
|
||||
case "enable":
|
||||
elements[attribute].removeAttribute("disabled");
|
||||
elements[attribute].dispatchEvent(new Event('change'));
|
||||
break;
|
||||
case "unvalue":
|
||||
elements[attribute].value = "";
|
||||
break;
|
||||
case "check":
|
||||
elements[attribute].value = "true";
|
||||
break;
|
||||
case "uncheck":
|
||||
elements[attribute].value = "false";
|
||||
break;
|
||||
}
|
||||
};
|
||||
for (const field in fields) {
|
||||
for (const attribute in fields[field]) {
|
||||
const attr = fields[field][attribute];
|
||||
if (Array.isArray(attr)) {
|
||||
attr.forEach(action => {
|
||||
if (elements[field].value === "true") {
|
||||
actionHandler(action, attribute);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const condition = attr.if.some(c => {
|
||||
return elements[c].value === "true";
|
||||
});
|
||||
if (condition) {
|
||||
for (const thenAction in attr.then) {
|
||||
attr.then[thenAction].forEach(action => {
|
||||
actionHandler(action, thenAction);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const elseAction in attr.else) {
|
||||
attr.else[elseAction].forEach(action => {
|
||||
actionHandler(action, elseAction);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
form.addEventListener("reset", () => {
|
||||
for (const key in fields) {
|
||||
if (Object.hasOwnProperty.call(fields, key)) {
|
||||
const element = form.elements[key];
|
||||
element.setAttribute("value", "");
|
||||
element.removeAttribute("disabled");
|
||||
element.dispatchEvent(new Event("change"));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -113,6 +113,13 @@ window.ls.router
|
|||
scope: "console",
|
||||
project: true
|
||||
})
|
||||
.add("/console/database/usage", {
|
||||
template: function(window) {
|
||||
return window.location.pathname + window.location.search + '&version=' + APP_ENV.CACHEBUSTER;
|
||||
},
|
||||
scope: "console",
|
||||
project: true
|
||||
})
|
||||
.add("/console/database/collection", {
|
||||
template: function(window) {
|
||||
return window.location.pathname + window.location.search + '&version=' + APP_ENV.CACHEBUSTER;
|
||||
|
|
|
@ -13,11 +13,20 @@
|
|||
case 'integer':
|
||||
value = parseInt(value);
|
||||
break;
|
||||
case 'float':
|
||||
value = parseFloat(parseFloat(value).toFixed(2));
|
||||
break;
|
||||
case 'numeric':
|
||||
value = Number(value);
|
||||
break;
|
||||
case 'float':
|
||||
value = parseFloat(value);
|
||||
break;
|
||||
case 'string':
|
||||
value = value.toString();
|
||||
if (value.length === 0) {
|
||||
value = null;
|
||||
}
|
||||
break;
|
||||
case 'json':
|
||||
value = (value) ? JSON.parse(value) : [];
|
||||
|
@ -105,7 +114,7 @@
|
|||
json[name] = element.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
json[name] = cast(json[name], castTo); // Apply casting
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
(function(window) {
|
||||
(function (window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-chart",
|
||||
controller: function(element, container, date, document) {
|
||||
controller: function (element, container, date, document) {
|
||||
let wrapper = document.createElement("div");
|
||||
let child = document.createElement("canvas");
|
||||
let sources = element.getAttribute('data-forms-chart');
|
||||
|
@ -19,7 +19,7 @@
|
|||
element.parentNode.insertBefore(wrapper, element.nextSibling);
|
||||
|
||||
wrapper.classList.add('content');
|
||||
|
||||
|
||||
child.width = width;
|
||||
child.height = height;
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
let chart = null;
|
||||
|
||||
let check = function() {
|
||||
let check = function () {
|
||||
|
||||
let config = {
|
||||
type: "line",
|
||||
|
@ -41,7 +41,7 @@
|
|||
responsive: true,
|
||||
hover: {
|
||||
mode: "nearest",
|
||||
intersect: true
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
|
@ -52,6 +52,7 @@
|
|||
min: 0,
|
||||
ticks: {
|
||||
count: ticksCount,
|
||||
fontColor: "#8f8f8f"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -89,12 +90,12 @@
|
|||
config.data.datasets[i].data = [0, 0, 0, 0, 0, 0, 0];
|
||||
config.data.datasets[i].fill = true;
|
||||
|
||||
if(!data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let dateFormat = (value.range && range[value.range]) ? range[value.range] : 'd F Y';
|
||||
|
||||
|
||||
for (let x = 0; x < data.length; x++) {
|
||||
if(data[x].value > highest) {
|
||||
highest = data[x].value;
|
||||
|
@ -128,4 +129,4 @@
|
|||
element.addEventListener('change', check);
|
||||
}
|
||||
});
|
||||
})(window);
|
||||
})(window);
|
|
@ -3,13 +3,16 @@
|
|||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-clone",
|
||||
controller: function(element, document, view) {
|
||||
controller: function(element, document, view, expression) {
|
||||
element.removeAttribute('data-forms-clone');
|
||||
view.render(element);
|
||||
var template = element.innerHTML.toString();
|
||||
var label = element.dataset["label"] || "Add";
|
||||
var icon = element.dataset["icon"] || null;
|
||||
var target = element.dataset["target"] || null;
|
||||
var target = expression.parse(element.dataset["target"] || null);
|
||||
var first = parseInt(element.dataset["first"] || 1);
|
||||
var button = document.createElement("button");
|
||||
var debug = element.dataset["debug"] || false;
|
||||
|
||||
button.type = "button";
|
||||
button.innerText = " " + label + " ";
|
||||
|
@ -42,6 +45,11 @@
|
|||
|
||||
view.render(clone);
|
||||
|
||||
if(debug) {
|
||||
console.log('Debug: clone: ', clone);
|
||||
console.log('Debug: target: ', target);
|
||||
}
|
||||
|
||||
if (target) {
|
||||
target.appendChild(clone);
|
||||
} else {
|
||||
|
|
16
public/scripts/views/forms/ip-address.js
Normal file
16
public/scripts/views/forms/ip-address.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-ip-address",
|
||||
controller: function(element) {
|
||||
element.setAttribute("minlength", "7");
|
||||
element.setAttribute("maxlength", "15");
|
||||
element.setAttribute("size", "15");
|
||||
element.setAttribute("autocomplete", "off");
|
||||
element.setAttribute("title", "Please provide IPv4 or IPv6 address");
|
||||
|
||||
element.setAttribute("pattern", "((^\\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))");
|
||||
}
|
||||
});
|
||||
})(window);
|
15
public/scripts/views/forms/required.js
Normal file
15
public/scripts/views/forms/required.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-required",
|
||||
controller: function(element, expression) {
|
||||
const isRequired = expression.parse(element.getAttribute('data-forms-required')) === "true";
|
||||
if (isRequired) {
|
||||
element.setAttribute("required", true);
|
||||
} else {
|
||||
element.removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
})(window);
|
16
public/scripts/views/forms/selected.js
Normal file
16
public/scripts/views/forms/selected.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
(function(window) {
|
||||
"use strict";
|
||||
|
||||
window.ls.container.get("view").add({
|
||||
selector: "data-forms-selected",
|
||||
controller: function(element, expression) {
|
||||
const isSelected = expression.parse(element.getAttribute('data-forms-selected')) === element.getAttribute('value');
|
||||
|
||||
if (isSelected) {
|
||||
element.setAttribute("selected", true);
|
||||
} else {
|
||||
element.removeAttribute("selected");
|
||||
}
|
||||
}
|
||||
});
|
||||
})(window);
|
|
@ -21,8 +21,15 @@
|
|||
|
||||
let syncB = function() {
|
||||
input.checked = (element.value === "true");
|
||||
|
||||
if (element.disabled) {
|
||||
input.setAttribute("disabled", true);
|
||||
} else {
|
||||
input.removeAttribute("disabled");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
input.addEventListener("input", syncA);
|
||||
input.addEventListener("change", syncA);
|
||||
|
||||
|
|
|
@ -154,12 +154,12 @@
|
|||
case 'document':
|
||||
document[element.key] = element.default || {'$id': '', '$collection': '', '$permissions': {}};
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
document[element.key] = null;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(element.array) {
|
||||
document[element.key] = [];
|
||||
}
|
||||
|
@ -395,7 +395,9 @@
|
|||
"failureParam" +
|
||||
parsedFailure[i].charAt(0).toUpperCase() +
|
||||
parsedFailure[i].slice(1),
|
||||
{}
|
||||
{
|
||||
text: exception.message ?? undefined
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -78,7 +78,9 @@
|
|||
.icon-ok-circled:before { content: '\e81a'; } /* '' */
|
||||
.icon-warning:before { content: '\e81b'; } /* '' */
|
||||
.icon-mail:before { content: '\e81c'; } /* '' */
|
||||
.icon-email:before { content: '\e81c'; } /* '' */
|
||||
.icon-link:before { content: '\e81d'; } /* '' */
|
||||
.icon-url:before { content: '\e81d'; } /* '' */
|
||||
.icon-key-inv:before { content: '\e81e'; } /* '' */
|
||||
.icon-trash:before { content: '\e81f'; } /* '' */
|
||||
.icon-download:before { content: '\e820'; } /* '' */
|
||||
|
@ -134,6 +136,8 @@
|
|||
.icon-string:before { content: '\e852'; } /* '' */
|
||||
.icon-integer:before { content: '\e853'; } /* '' */
|
||||
.icon-float:before { content: '\e854'; } /* '' */
|
||||
.icon-double:before { content: '\e854'; } /* '' */
|
||||
.icon-enum:before { content: '\e812'; } /* '' */
|
||||
.icon-ip:before { content: '\e855'; } /* '' */
|
||||
.icon-more:before { content: '\e856'; } /* '' */
|
||||
.icon-key:before { content: '\e857'; } /* '' */
|
||||
|
|
|
@ -49,7 +49,7 @@ class PDO extends PDONative
|
|||
return $this->pdo->setAttribute($attribute, $value);
|
||||
}
|
||||
|
||||
public function prepare($statement, $driver_options = NULL)
|
||||
public function prepare($statement, $driver_options = null)
|
||||
{
|
||||
return new PDOStatement($this, $this->pdo->prepare($statement, []));
|
||||
}
|
||||
|
@ -107,4 +107,4 @@ class PDO extends PDONative
|
|||
|
||||
return $this->pdo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,10 +100,10 @@ class PDOStatement extends PDOStatementNative
|
|||
|
||||
/**
|
||||
* Fetch All
|
||||
*
|
||||
*
|
||||
* @param int $fetch_style
|
||||
* @param mixed $fetch_args
|
||||
*
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function fetchAll(int $fetch_style = PDO::FETCH_BOTH, mixed ...$fetch_args)
|
||||
|
@ -112,4 +112,4 @@ class PDOStatement extends PDOStatementNative
|
|||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Appwrite\OpenSSL;
|
|||
|
||||
class OpenSSL
|
||||
{
|
||||
const CIPHER_AES_128_GCM = 'aes-128-gcm';
|
||||
public const CIPHER_AES_128_GCM = 'aes-128-gcm';
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
|
|
|
@ -196,7 +196,7 @@ class Response extends SwooleResponse
|
|||
->setModel(new BaseList('Documents List', self::MODEL_DOCUMENT_LIST, 'documents', self::MODEL_DOCUMENT))
|
||||
->setModel(new BaseList('Users List', self::MODEL_USER_LIST, 'users', self::MODEL_USER))
|
||||
->setModel(new BaseList('Sessions List', self::MODEL_SESSION_LIST, 'sessions', self::MODEL_SESSION))
|
||||
->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG, false))
|
||||
->setModel(new BaseList('Logs List', self::MODEL_LOG_LIST, 'logs', self::MODEL_LOG))
|
||||
->setModel(new BaseList('Files List', self::MODEL_FILE_LIST, 'files', self::MODEL_FILE))
|
||||
->setModel(new BaseList('Buckets List', self::MODEL_BUCKET_LIST, 'buckets', self::MODEL_BUCKET))
|
||||
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
|
||||
|
|
|
@ -16,35 +16,35 @@ class UsageCollection extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('documents.count', [
|
||||
->addRule('documentsCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.create', [
|
||||
->addRule('documentsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.read', [
|
||||
->addRule('documentsRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.update', [
|
||||
->addRule('documentsUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.delete', [
|
||||
->addRule('documentsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents deleted.',
|
||||
'default' => [],
|
||||
|
|
|
@ -16,70 +16,70 @@ class UsageDatabase extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('documents.count', [
|
||||
->addRule('documentsCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of documents.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.count', [
|
||||
->addRule('collectionsCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of collections.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.create', [
|
||||
->addRule('documentsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.read', [
|
||||
->addRule('documentsRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.update', [
|
||||
->addRule('documentsUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('documents.delete', [
|
||||
->addRule('documentsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for documents deleted.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.create', [
|
||||
->addRule('collectionsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.read', [
|
||||
->addRule('collectionsRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.update', [
|
||||
->addRule('collectionsUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('collections.delete', [
|
||||
->addRule('collectionsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for collections delete.',
|
||||
'default' => [],
|
||||
|
|
|
@ -16,21 +16,21 @@ class UsageFunctions extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('functions.executions', [
|
||||
->addRule('functionsExecutions', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for function executions.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('functions.failures', [
|
||||
->addRule('functionsFailures', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for function execution failures.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('functions.compute', [
|
||||
->addRule('functionsCompute', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for function execution duration.',
|
||||
'default' => [],
|
||||
|
|
|
@ -16,56 +16,56 @@ class UsageUsers extends Model
|
|||
'default' => '',
|
||||
'example' => '30d',
|
||||
])
|
||||
->addRule('users.count', [
|
||||
->addRule('usersCount', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for total number of users.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.create', [
|
||||
->addRule('usersCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.read', [
|
||||
->addRule('usersRead', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users read.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.update', [
|
||||
->addRule('usersUpdate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users updated.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('users.delete', [
|
||||
->addRule('usersDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for users deleted.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessions.create', [
|
||||
->addRule('sessionsCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for sessions created.',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessions.provider.create', [
|
||||
->addRule('sessionsProviderCreate', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for sessions created for a provider ( email, anonymous or oauth2 ).',
|
||||
'default' => [],
|
||||
'example' => new \stdClass,
|
||||
'array' => true
|
||||
])
|
||||
->addRule('sessions.delete', [
|
||||
->addRule('sessionsDelete', [
|
||||
'type' => Response::MODEL_METRIC_LIST,
|
||||
'description' => 'Aggregated stats for sessions deleted.',
|
||||
'default' => [],
|
||||
|
|
|
@ -283,10 +283,10 @@ trait AccountBase
|
|||
$this->assertEquals('', $response['body']['sessions'][0]['deviceBrand']);
|
||||
$this->assertEquals('', $response['body']['sessions'][0]['deviceModel']);
|
||||
$this->assertEquals($response['body']['sessions'][0]['ip'], filter_var($response['body']['sessions'][0]['ip'], FILTER_VALIDATE_IP));
|
||||
|
||||
|
||||
$this->assertEquals('--', $response['body']['sessions'][0]['countryCode']);
|
||||
$this->assertEquals('Unknown', $response['body']['sessions'][0]['countryName']);
|
||||
|
||||
|
||||
$this->assertEquals(true, $response['body']['sessions'][0]['current']);
|
||||
|
||||
/**
|
||||
|
@ -325,7 +325,8 @@ trait AccountBase
|
|||
$this->assertIsArray($response['body']['logs']);
|
||||
$this->assertNotEmpty($response['body']['logs']);
|
||||
$this->assertCount(2, $response['body']['logs']);
|
||||
|
||||
$this->assertIsNumeric($response['body']['sum']);
|
||||
|
||||
$this->assertContains($response['body']['logs'][0]['event'], ['account.create', 'account.sessions.create']);
|
||||
$this->assertEquals($response['body']['logs'][0]['ip'], filter_var($response['body']['logs'][0]['ip'], FILTER_VALIDATE_IP));
|
||||
$this->assertIsNumeric($response['body']['logs'][0]['time']);
|
||||
|
@ -344,7 +345,7 @@ trait AccountBase
|
|||
$this->assertEquals('', $response['body']['logs'][0]['deviceBrand']);
|
||||
$this->assertEquals('', $response['body']['logs'][0]['deviceModel']);
|
||||
$this->assertEquals($response['body']['logs'][0]['ip'], filter_var($response['body']['logs'][0]['ip'], FILTER_VALIDATE_IP));
|
||||
|
||||
|
||||
$this->assertEquals('--', $response['body']['logs'][0]['countryCode']);
|
||||
$this->assertEquals('Unknown', $response['body']['logs'][0]['countryName']);
|
||||
|
||||
|
@ -366,10 +367,61 @@ trait AccountBase
|
|||
$this->assertEquals('', $response['body']['logs'][1]['deviceBrand']);
|
||||
$this->assertEquals('', $response['body']['logs'][1]['deviceModel']);
|
||||
$this->assertEquals($response['body']['logs'][1]['ip'], filter_var($response['body']['logs'][1]['ip'], FILTER_VALIDATE_IP));
|
||||
|
||||
|
||||
$this->assertEquals('--', $response['body']['logs'][1]['countryCode']);
|
||||
$this->assertEquals('Unknown', $response['body']['logs'][1]['countryName']);
|
||||
|
||||
|
||||
$responseLimit = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
]), [
|
||||
'limit' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($responseLimit['headers']['status-code'], 200);
|
||||
$this->assertIsArray($responseLimit['body']['logs']);
|
||||
$this->assertNotEmpty($responseLimit['body']['logs']);
|
||||
$this->assertCount(1, $responseLimit['body']['logs']);
|
||||
$this->assertIsNumeric($responseLimit['body']['sum']);
|
||||
|
||||
$this->assertEquals($response['body']['logs'][0], $responseLimit['body']['logs'][0]);
|
||||
|
||||
$responseOffset = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
]), [
|
||||
'offset' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($responseOffset['headers']['status-code'], 200);
|
||||
$this->assertIsArray($responseOffset['body']['logs']);
|
||||
$this->assertNotEmpty($responseOffset['body']['logs']);
|
||||
$this->assertCount(1, $responseOffset['body']['logs']);
|
||||
$this->assertIsNumeric($responseOffset['body']['sum']);
|
||||
|
||||
$this->assertEquals($response['body']['logs'][1], $responseOffset['body']['logs'][0]);
|
||||
|
||||
$responseLimitOffset = $this->client->call(Client::METHOD_GET, '/account/logs', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session,
|
||||
]), [
|
||||
'limit' => 1,
|
||||
'offset' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($responseLimitOffset['headers']['status-code'], 200);
|
||||
$this->assertIsArray($responseLimitOffset['body']['logs']);
|
||||
$this->assertNotEmpty($responseLimitOffset['body']['logs']);
|
||||
$this->assertCount(1, $responseLimitOffset['body']['logs']);
|
||||
$this->assertIsNumeric($responseLimitOffset['body']['sum']);
|
||||
|
||||
$this->assertEquals($response['body']['logs'][1], $responseLimitOffset['body']['logs'][0]);
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
|
@ -595,11 +595,27 @@ trait DatabaseBase
|
|||
'attributes' => ['title'],
|
||||
]);
|
||||
|
||||
$this->assertEquals($titleIndex['headers']['status-code'], 201);
|
||||
$this->assertEquals($titleIndex['body']['key'], 'titleIndex');
|
||||
$this->assertEquals($titleIndex['body']['type'], 'fulltext');
|
||||
$this->assertEquals(201, $titleIndex['headers']['status-code']);
|
||||
$this->assertEquals('titleIndex', $titleIndex['body']['key']);
|
||||
$this->assertEquals('fulltext', $titleIndex['body']['type']);
|
||||
$this->assertCount(1, $titleIndex['body']['attributes']);
|
||||
$this->assertEquals($titleIndex['body']['attributes'][0], 'title');
|
||||
$this->assertEquals('title', $titleIndex['body']['attributes'][0]);
|
||||
|
||||
$releaseYearIndex = $this->client->call(Client::METHOD_POST, '/database/collections/' . $data['moviesId'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'indexId' => 'releaseYear',
|
||||
'type' => 'key',
|
||||
'attributes' => ['releaseYear'],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $releaseYearIndex['headers']['status-code']);
|
||||
$this->assertEquals('releaseYear', $releaseYearIndex['body']['key']);
|
||||
$this->assertEquals('key', $releaseYearIndex['body']['type']);
|
||||
$this->assertCount(1, $releaseYearIndex['body']['attributes']);
|
||||
$this->assertEquals('releaseYear', $releaseYearIndex['body']['attributes'][0]);
|
||||
|
||||
// wait for database worker to create index
|
||||
sleep(2);
|
||||
|
@ -611,8 +627,11 @@ trait DatabaseBase
|
|||
]), []);
|
||||
|
||||
$this->assertIsArray($movies['body']['indexes']);
|
||||
$this->assertCount(1, $movies['body']['indexes']);
|
||||
$this->assertEquals($movies['body']['indexes'][0]['key'], $titleIndex['body']['key']);
|
||||
$this->assertCount(2, $movies['body']['indexes']);
|
||||
$this->assertEquals($titleIndex['body']['key'], $movies['body']['indexes'][0]['key']);
|
||||
$this->assertEquals($releaseYearIndex['body']['key'], $movies['body']['indexes'][1]['key']);
|
||||
$this->assertEquals('available', $movies['body']['indexes'][0]['status']);
|
||||
$this->assertEquals('available', $movies['body']['indexes'][1]['status']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -1024,93 +1043,77 @@ trait DatabaseBase
|
|||
/**
|
||||
* @depends testCreateDocument
|
||||
*/
|
||||
// public function testDocumentsListSuccessSearch(array $data):array
|
||||
// {
|
||||
// $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// ], $this->getHeaders()), [
|
||||
// 'queries' => ['title.search("Captain America")'],
|
||||
// ]);
|
||||
public function testDocumentsListQueries(array $data):array
|
||||
{
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['title.search("Captain America")'],
|
||||
]);
|
||||
|
||||
// var_dump($documents);
|
||||
$this->assertEquals($documents['headers']['status-code'], 200);
|
||||
$this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']);
|
||||
$this->assertCount(1, $documents['body']['documents']);
|
||||
|
||||
// $this->assertEquals($documents['headers']['status-code'], 200);
|
||||
// $this->assertEquals(1944, $documents['body']['documents'][0]['releaseYear']);
|
||||
// $this->assertCount(1, $documents['body']['documents']);
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['title.search("Homecoming")'],
|
||||
]);
|
||||
|
||||
// $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// ], $this->getHeaders()), [
|
||||
// 'queries' => ['title.search("Homecoming")'],
|
||||
// ]);
|
||||
$this->assertEquals($documents['headers']['status-code'], 200);
|
||||
$this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']);
|
||||
$this->assertCount(1, $documents['body']['documents']);
|
||||
|
||||
// $this->assertEquals($documents['headers']['status-code'], 200);
|
||||
// $this->assertEquals(2017, $documents['body']['documents'][0]['releaseYear']);
|
||||
// $this->assertCount(1, $documents['body']['documents']);
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['title.search("spider")'],
|
||||
]);
|
||||
|
||||
// $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// ], $this->getHeaders()), [
|
||||
// 'queries' => ['title.search("spider")'],
|
||||
// ]);
|
||||
$this->assertEquals($documents['headers']['status-code'], 200);
|
||||
$this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']);
|
||||
$this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']);
|
||||
$this->assertCount(2, $documents['body']['documents']);
|
||||
|
||||
// $this->assertEquals($documents['headers']['status-code'], 200);
|
||||
// $this->assertEquals(2019, $documents['body']['documents'][0]['releaseYear']);
|
||||
// $this->assertEquals(2017, $documents['body']['documents'][1]['releaseYear']);
|
||||
// $this->assertCount(2, $documents['body']['documents']);
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['releaseYear.equal(1944)'],
|
||||
]);
|
||||
|
||||
// return [];
|
||||
// }
|
||||
// TODO@kodumbeats test for empty searches and misformatted queries
|
||||
$this->assertCount(1, $documents['body']['documents']);
|
||||
$this->assertEquals('Captain America', $documents['body']['documents'][0]['title']);
|
||||
|
||||
/**
|
||||
* @depends testCreateDocument
|
||||
*/
|
||||
// public function testListDocumentsFilters(array $data):array
|
||||
// {
|
||||
// $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// ], $this->getHeaders()), [
|
||||
// 'filters' => [
|
||||
// 'actors.firstName=Tom'
|
||||
// ],
|
||||
// ]);
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['releaseYear.notEqual(1944)'],
|
||||
]);
|
||||
|
||||
// $this->assertCount(2, $documents['body']['documents']);
|
||||
// $this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['name']);
|
||||
// $this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['name']);
|
||||
$this->assertCount(2, $documents['body']['documents']);
|
||||
$this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['title']);
|
||||
$this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['title']);
|
||||
|
||||
// $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// ], $this->getHeaders()), [
|
||||
// 'filters' => [
|
||||
// 'releaseYear=1944'
|
||||
// ],
|
||||
// ]);
|
||||
/**
|
||||
* Test for Failure
|
||||
*/
|
||||
$documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => ['actors.equal("Tom Holland")'],
|
||||
]);
|
||||
$this->assertEquals(400, $documents['headers']['status-code']);
|
||||
$this->assertEquals('Index not found: actors', $documents['body']['message']);
|
||||
|
||||
// $this->assertCount(1, $documents['body']['documents']);
|
||||
// $this->assertEquals('Captain America', $documents['body']['documents'][0]['name']);
|
||||
|
||||
// $documents = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $this->getProject()['$id'],
|
||||
// ], $this->getHeaders()), [
|
||||
// 'filters' => [
|
||||
// 'releaseYear!=1944'
|
||||
// ],
|
||||
// ]);
|
||||
|
||||
// $this->assertCount(2, $documents['body']['documents']);
|
||||
// $this->assertEquals('Spider-Man: Far From Home', $documents['body']['documents'][0]['name']);
|
||||
// $this->assertEquals('Spider-Man: Homecoming', $documents['body']['documents'][1]['name']);
|
||||
|
||||
// return [];
|
||||
// }
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDocument
|
||||
|
|
|
@ -12,7 +12,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
use ProjectCustom;
|
||||
use SideConsole;
|
||||
|
||||
public function testCreateCollection():array
|
||||
public function testCreateCollection(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -33,7 +33,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
|
||||
return ['moviesId' => $movies['body']['$id']];
|
||||
}
|
||||
|
||||
|
||||
public function testGetDatabaseUsage()
|
||||
{
|
||||
/**
|
||||
|
@ -48,7 +48,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
@ -63,16 +63,16 @@ class DatabaseConsoleClientTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 11);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['documents.count']);
|
||||
$this->assertIsArray($response['body']['collections.count']);
|
||||
$this->assertIsArray($response['body']['documents.create']);
|
||||
$this->assertIsArray($response['body']['documents.read']);
|
||||
$this->assertIsArray($response['body']['documents.update']);
|
||||
$this->assertIsArray($response['body']['documents.delete']);
|
||||
$this->assertIsArray($response['body']['collections.create']);
|
||||
$this->assertIsArray($response['body']['collections.read']);
|
||||
$this->assertIsArray($response['body']['collections.update']);
|
||||
$this->assertIsArray($response['body']['collections.delete']);
|
||||
$this->assertIsArray($response['body']['documentsCount']);
|
||||
$this->assertIsArray($response['body']['collectionsCount']);
|
||||
$this->assertIsArray($response['body']['documentsCreate']);
|
||||
$this->assertIsArray($response['body']['documentsRead']);
|
||||
$this->assertIsArray($response['body']['documentsUpdate']);
|
||||
$this->assertIsArray($response['body']['documentsDelete']);
|
||||
$this->assertIsArray($response['body']['collectionsCreate']);
|
||||
$this->assertIsArray($response['body']['collectionsRead']);
|
||||
$this->assertIsArray($response['body']['collectionsUpdate']);
|
||||
$this->assertIsArray($response['body']['collectionsDelete']);
|
||||
}
|
||||
|
||||
|
||||
|
@ -85,7 +85,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/' . $data['moviesId'] . '/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
|
@ -106,7 +106,7 @@ class DatabaseConsoleClientTest extends Scope
|
|||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/'.$data['moviesId'].'/usage', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/database/' . $data['moviesId'] . '/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
|
@ -116,10 +116,64 @@ class DatabaseConsoleClientTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 6);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['documents.count']);
|
||||
$this->assertIsArray($response['body']['documents.create']);
|
||||
$this->assertIsArray($response['body']['documents.read']);
|
||||
$this->assertIsArray($response['body']['documents.update']);
|
||||
$this->assertIsArray($response['body']['documents.delete']);
|
||||
$this->assertIsArray($response['body']['documentsCount']);
|
||||
$this->assertIsArray($response['body']['documentsCreate']);
|
||||
$this->assertIsArray($response['body']['documentsRead']);
|
||||
$this->assertIsArray($response['body']['documentsUpdate']);
|
||||
$this->assertIsArray($response['body']['documentsDelete']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
public function testGetCollectionLogs(array $data)
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'limit' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'offset' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'offset' => 1,
|
||||
'limit' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,6 +193,22 @@ class DatabaseCustomServerTest extends Scope
|
|||
// Wait for database worker to finish creating attributes
|
||||
sleep(2);
|
||||
|
||||
// Creating document to ensure cache is purged on schema change
|
||||
$document = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'documentId' => 'unique()',
|
||||
'data' => [
|
||||
'firstName' => 'lorem',
|
||||
'lastName' => 'ipsum',
|
||||
'unneeded' => 'dolor'
|
||||
],
|
||||
'read' => ['role:all'],
|
||||
'write' => ['role:all'],
|
||||
]);
|
||||
|
||||
$index = $this->client->call(Client::METHOD_POST, '/database/collections/' . $actors['body']['$id'] . '/indexes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -236,6 +252,15 @@ class DatabaseCustomServerTest extends Scope
|
|||
|
||||
sleep(2);
|
||||
|
||||
// Check document to ensure cache is purged on schema change
|
||||
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $actors['body']['$id'] . '/documents/' . $document['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]));
|
||||
|
||||
$this->assertNotContains($unneededId, $document['body']);
|
||||
|
||||
$collection = $this->client->call(Client::METHOD_GET, '/database/collections/' . $actors['body']['$id'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
|
|
@ -12,7 +12,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
use ProjectCustom;
|
||||
use SideConsole;
|
||||
|
||||
public function testCreateFunction():array
|
||||
public function testCreateFunction(): array
|
||||
{
|
||||
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
@ -20,7 +20,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'functionId' => 'unique()',
|
||||
'name' => 'Test',
|
||||
'execute' => ['user:'.$this->getUser()['$id']],
|
||||
'execute' => ['user:' . $this->getUser()['$id']],
|
||||
'runtime' => 'php-8.0',
|
||||
'vars' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
|
@ -41,7 +41,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
'functionId' => $function['body']['$id']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateFunction
|
||||
*/
|
||||
|
@ -51,7 +51,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/usage', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
|
@ -73,7 +73,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/usage', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
|
@ -83,9 +83,8 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 4);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['functions.executions']);
|
||||
$this->assertIsArray($response['body']['functions.failures']);
|
||||
$this->assertIsArray($response['body']['functions.compute']);
|
||||
$this->assertIsArray($response['body']['functionsExecutions']);
|
||||
$this->assertIsArray($response['body']['functionsFailures']);
|
||||
$this->assertIsArray($response['body']['functionsCompute']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ class HealthCustomServerTest extends Scope
|
|||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testHTTPSuccess():array
|
||||
public function testHTTPSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -30,11 +30,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testDBSuccess():array
|
||||
public function testDBSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -50,11 +50,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testCacheSuccess():array
|
||||
public function testCacheSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -70,11 +70,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testTimeSuccess():array
|
||||
public function testTimeSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -94,11 +94,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testWebhooksSuccess():array
|
||||
public function testWebhooksSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -115,11 +115,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testLogsSuccess():array
|
||||
public function testLogsSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -136,11 +136,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testUsageSuccess():array
|
||||
public function testUsageSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -157,11 +157,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testCertificatesSuccess():array
|
||||
public function testCertificatesSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -178,11 +178,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testStorageLocalSuccess():array
|
||||
public function testStorageLocalSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -198,11 +198,11 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function testStorageAntiVirusSuccess():array
|
||||
public function testStorageAntiVirusSuccess(): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
|
@ -220,7 +220,7 @@ class HealthCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ trait RealtimeBase
|
|||
$this->expectException(ConnectionException::class); // Check if server disconnnected client
|
||||
$client->close();
|
||||
|
||||
$client = new WebSocketClient('ws://appwrite-traefik/v1/realtime', [
|
||||
$client = new WebSocketClient('ws://appwrite-traefik/v1/realtime?channels[]=files"', [
|
||||
'headers' => [
|
||||
'Origin' => 'appwrite.test'
|
||||
]
|
||||
|
|
|
@ -26,7 +26,7 @@ class StorageConsoleClientTest extends Scope
|
|||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
@ -102,4 +102,4 @@ class StorageConsoleClientTest extends Scope
|
|||
$this->assertIsArray($response['body']['filesDelete']);
|
||||
$this->assertIsArray($response['body']['filesStorage']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,8 +73,8 @@ trait TeamsBaseClient
|
|||
$this->assertEquals('Invitation to '.$teamName.' Team at '.$this->getProject()['name'], $lastEmail['subject']);
|
||||
|
||||
$secret = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
|
||||
$membershipUid = substr($lastEmail['text'], strpos($lastEmail['text'], '?membershipId=', 0) + 14, 13);
|
||||
$userUid = substr($lastEmail['text'], strpos($lastEmail['text'], '&userId=', 0) + 8, 13);
|
||||
$membershipUid = substr($lastEmail['text'], strpos($lastEmail['text'], '?membershipId=', 0) + 14, 20);
|
||||
$userUid = substr($lastEmail['text'], strpos($lastEmail['text'], '&userId=', 0) + 8, 20);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
|
|
@ -204,14 +204,6 @@ trait UsersBase
|
|||
$this->assertEquals($sessions['headers']['status-code'], 200);
|
||||
$this->assertIsArray($sessions['body']);
|
||||
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']);
|
||||
|
||||
$users = $this->client->call(Client::METHOD_GET, '/users', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
@ -428,6 +420,61 @@ trait UsersBase
|
|||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testGetUser
|
||||
*/
|
||||
public function testGetLogs(array $data): void
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'limit' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'offset' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
|
||||
$logs = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/logs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'offset' => 1,
|
||||
'limit' => 1
|
||||
]);
|
||||
|
||||
$this->assertEquals($logs['headers']['status-code'], 200);
|
||||
$this->assertIsArray($logs['body']['logs']);
|
||||
$this->assertLessThanOrEqual(1, count($logs['body']['logs']));
|
||||
$this->assertIsNumeric($logs['body']['sum']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testGetUser
|
||||
*/
|
||||
|
|
|
@ -37,7 +37,7 @@ class UsersConsoleClientTest extends Scope
|
|||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 400);
|
||||
|
||||
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
@ -52,15 +52,15 @@ class UsersConsoleClientTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 9);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['users.count']);
|
||||
$this->assertIsArray($response['body']['users.create']);
|
||||
$this->assertIsArray($response['body']['users.read']);
|
||||
$this->assertIsArray($response['body']['users.update']);
|
||||
$this->assertIsArray($response['body']['users.delete']);
|
||||
$this->assertIsArray($response['body']['sessions.create']);
|
||||
$this->assertIsArray($response['body']['sessions.provider.create']);
|
||||
$this->assertIsArray($response['body']['sessions.delete']);
|
||||
|
||||
$this->assertIsArray($response['body']['usersCount']);
|
||||
$this->assertIsArray($response['body']['usersCreate']);
|
||||
$this->assertIsArray($response['body']['usersRead']);
|
||||
$this->assertIsArray($response['body']['usersUpdate']);
|
||||
$this->assertIsArray($response['body']['usersDelete']);
|
||||
$this->assertIsArray($response['body']['sessionsCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsProviderCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsDelete']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/usage', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
|
@ -71,13 +71,13 @@ class UsersConsoleClientTest extends Scope
|
|||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals(count($response['body']), 9);
|
||||
$this->assertEquals($response['body']['range'], '24h');
|
||||
$this->assertIsArray($response['body']['users.count']);
|
||||
$this->assertIsArray($response['body']['users.create']);
|
||||
$this->assertIsArray($response['body']['users.read']);
|
||||
$this->assertIsArray($response['body']['users.update']);
|
||||
$this->assertIsArray($response['body']['users.delete']);
|
||||
$this->assertIsArray($response['body']['sessions.create']);
|
||||
$this->assertIsArray($response['body']['sessions.provider.create']);
|
||||
$this->assertIsArray($response['body']['sessions.delete']);
|
||||
$this->assertIsArray($response['body']['usersCount']);
|
||||
$this->assertIsArray($response['body']['usersCreate']);
|
||||
$this->assertIsArray($response['body']['usersRead']);
|
||||
$this->assertIsArray($response['body']['usersUpdate']);
|
||||
$this->assertIsArray($response['body']['usersDelete']);
|
||||
$this->assertIsArray($response['body']['sessionsCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsProviderCreate']);
|
||||
$this->assertIsArray($response['body']['sessionsDelete']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -662,8 +662,8 @@ trait WebhooksBase
|
|||
$lastEmail = $this->getLastEmail();
|
||||
|
||||
$secret = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
|
||||
$membershipUid = substr($lastEmail['text'], strpos($lastEmail['text'], '?membershipId=', 0) + 14, 13);
|
||||
$userUid = substr($lastEmail['text'], strpos($lastEmail['text'], '&userId=', 0) + 8, 13);
|
||||
$membershipUid = substr($lastEmail['text'], strpos($lastEmail['text'], '?membershipId=', 0) + 14, 20);
|
||||
$userUid = substr($lastEmail['text'], strpos($lastEmail['text'], '&userId=', 0) + 8, 20);
|
||||
|
||||
$webhook = $this->getLastRequest();
|
||||
|
||||
|
|
Loading…
Reference in a new issue