+
+
+
)
}
diff --git a/front/components/Objects/Modals/Course/Create/CreateCourse.tsx b/front/components/Objects/Modals/Course/Create/CreateCourse.tsx
index 5e7c0fc2..e9870b9f 100644
--- a/front/components/Objects/Modals/Course/Create/CreateCourse.tsx
+++ b/front/components/Objects/Modals/Course/Create/CreateCourse.tsx
@@ -43,7 +43,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) {
e.preventDefault();
setIsSubmitting(true);
let status = await createNewCourse(orgId, { name, description }, thumbnail);
- revalidateTags(['courses']);
+ revalidateTags(['courses'], orgslug);
setIsSubmitting(false);
if (status.org_id == orgId) {
diff --git a/front/components/Pages/Trail/TrailCourseElement.tsx b/front/components/Pages/Trail/TrailCourseElement.tsx
index dd32c5c8..c2d9ec6a 100644
--- a/front/components/Pages/Trail/TrailCourseElement.tsx
+++ b/front/components/Pages/Trail/TrailCourseElement.tsx
@@ -18,7 +18,7 @@ function TrailCourseElement(props: TrailCourseElementProps) {
// Close activity
let activity = await removeCourse(course_id, props.orgslug);
// Mutate course
- revalidateTags(['courses']);
+ revalidateTags(['courses'], props.orgslug);
// Mutate
mutate(`${getAPIUrl()}trail/org_slug/${props.orgslug}/trail`);
diff --git a/front/components/Security/AuthenticatedClientElement.tsx b/front/components/Security/AuthenticatedClientElement.tsx
index 8e442352..6ced1f88 100644
--- a/front/components/Security/AuthenticatedClientElement.tsx
+++ b/front/components/Security/AuthenticatedClientElement.tsx
@@ -9,7 +9,7 @@ interface AuthenticatedClientElementProps {
}
-function AuthenticatedClientElement(props: AuthenticatedClientElementProps) {
+export const AuthenticatedClientElement = (props: AuthenticatedClientElementProps) => {
const auth: any = React.useContext(AuthContext);
// Available roles
diff --git a/front/components/StyledElements/Wrappers/GeneralWrapper.tsx b/front/components/StyledElements/Wrappers/GeneralWrapper.tsx
index e6eb908c..5b42ad8e 100644
--- a/front/components/StyledElements/Wrappers/GeneralWrapper.tsx
+++ b/front/components/StyledElements/Wrappers/GeneralWrapper.tsx
@@ -3,6 +3,7 @@ function GeneralWrapperStyled({ children }: { children: React.ReactNode }) {
return (
{children}
)
}
diff --git a/front/package-lock.json b/front/package-lock.json
index 8f46da95..fa7645e5 100644
--- a/front/package-lock.json
+++ b/front/package-lock.json
@@ -51,7 +51,7 @@
"@types/styled-components": "^5.1.26",
"@types/uuid": "^9.0.0",
"autoprefixer": "^10.4.14",
- "eslint": "8.23.1",
+ "eslint": "^8.43.0",
"eslint-config-next": "^13.0.6",
"postcss": "^8.4.23",
"tailwindcss": "^3.3.2",
@@ -1930,16 +1930,40 @@
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
"integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
},
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz",
+ "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
"node_modules/@eslint/eslintrc": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
- "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz",
+ "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.4.0",
- "globals": "^13.15.0",
+ "espree": "^9.5.2",
+ "globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
@@ -1953,6 +1977,15 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/@eslint/js": {
+ "version": "8.43.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz",
+ "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
"node_modules/@floating-ui/core": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz",
@@ -1980,29 +2013,19 @@
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.10.4",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
- "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==",
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
+ "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
- "minimatch": "^3.0.4"
+ "minimatch": "^3.0.5"
},
"engines": {
"node": ">=10.10.0"
}
},
- "node_modules/@humanwhocodes/gitignore-to-minimatch": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz",
- "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==",
- "dev": true,
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -3770,9 +3793,9 @@
}
},
"node_modules/acorn": {
- "version": "8.8.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
- "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz",
+ "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@@ -4663,39 +4686,40 @@
}
},
"node_modules/eslint": {
- "version": "8.23.1",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz",
- "integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==",
+ "version": "8.43.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz",
+ "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==",
"dev": true,
"dependencies": {
- "@eslint/eslintrc": "^1.3.2",
- "@humanwhocodes/config-array": "^0.10.4",
- "@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.4.0",
+ "@eslint/eslintrc": "^2.0.3",
+ "@eslint/js": "8.43.0",
+ "@humanwhocodes/config-array": "^0.11.10",
"@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.1.1",
- "eslint-utils": "^3.0.0",
- "eslint-visitor-keys": "^3.3.0",
- "espree": "^9.4.0",
- "esquery": "^1.4.0",
+ "eslint-scope": "^7.2.0",
+ "eslint-visitor-keys": "^3.4.1",
+ "espree": "^9.5.2",
+ "esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
"find-up": "^5.0.0",
- "glob-parent": "^6.0.1",
- "globals": "^13.15.0",
- "globby": "^11.1.0",
- "grapheme-splitter": "^1.0.4",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
- "js-sdsl": "^4.1.4",
+ "is-path-inside": "^3.0.3",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
@@ -4703,7 +4727,6 @@
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
- "regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
@@ -5014,9 +5037,9 @@
}
},
"node_modules/eslint-scope": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
- "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
+ "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
@@ -5024,53 +5047,32 @@
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- }
- },
- "node_modules/eslint-utils": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
- "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
- "dev": true,
- "dependencies": {
- "eslint-visitor-keys": "^2.0.0"
- },
- "engines": {
- "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/mysticatea"
- },
- "peerDependencies": {
- "eslint": ">=5"
- }
- },
- "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
- "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
- "dev": true,
- "engines": {
- "node": ">=10"
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-visitor-keys": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
- "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
+ "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/espree": {
- "version": "9.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
- "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==",
+ "version": "9.5.2",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz",
+ "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==",
"dev": true,
"dependencies": {
"acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.3.0"
+ "eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -5080,9 +5082,9 @@
}
},
"node_modules/esquery": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
- "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
"dev": true,
"dependencies": {
"estraverse": "^5.1.0"
@@ -5475,9 +5477,9 @@
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
},
"node_modules/globals": {
- "version": "13.17.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
- "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
+ "version": "13.20.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+ "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
@@ -5546,10 +5548,10 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
- "node_modules/grapheme-splitter": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
"node_modules/has": {
@@ -5888,6 +5890,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
@@ -6001,12 +6012,6 @@
"jiti": "bin/jiti.js"
}
},
- "node_modules/js-sdsl": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz",
- "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==",
- "dev": true
- },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -7005,9 +7010,9 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"dev": true,
"engines": {
"node": ">=6"
@@ -7350,18 +7355,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/regexpp": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
- "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/mysticatea"
- }
- },
"node_modules/regexpu-core": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz",
@@ -9895,16 +9888,31 @@
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
"integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
},
+ "@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^3.3.0"
+ }
+ },
+ "@eslint-community/regexpp": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz",
+ "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==",
+ "dev": true
+ },
"@eslint/eslintrc": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
- "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz",
+ "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.4.0",
- "globals": "^13.15.0",
+ "espree": "^9.5.2",
+ "globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
@@ -9912,6 +9920,12 @@
"strip-json-comments": "^3.1.1"
}
},
+ "@eslint/js": {
+ "version": "8.43.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz",
+ "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==",
+ "dev": true
+ },
"@floating-ui/core": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.7.3.tgz",
@@ -9935,22 +9949,16 @@
}
},
"@humanwhocodes/config-array": {
- "version": "0.10.4",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz",
- "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==",
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
+ "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
"dev": true,
"requires": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
- "minimatch": "^3.0.4"
+ "minimatch": "^3.0.5"
}
},
- "@humanwhocodes/gitignore-to-minimatch": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz",
- "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==",
- "dev": true
- },
"@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -11206,9 +11214,9 @@
}
},
"acorn": {
- "version": "8.8.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
- "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz",
+ "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==",
"dev": true
},
"acorn-jsx": {
@@ -11820,39 +11828,40 @@
"dev": true
},
"eslint": {
- "version": "8.23.1",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz",
- "integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==",
+ "version": "8.43.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz",
+ "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==",
"dev": true,
"requires": {
- "@eslint/eslintrc": "^1.3.2",
- "@humanwhocodes/config-array": "^0.10.4",
- "@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.4.0",
+ "@eslint/eslintrc": "^2.0.3",
+ "@eslint/js": "8.43.0",
+ "@humanwhocodes/config-array": "^0.11.10",
"@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.1.1",
- "eslint-utils": "^3.0.0",
- "eslint-visitor-keys": "^3.3.0",
- "espree": "^9.4.0",
- "esquery": "^1.4.0",
+ "eslint-scope": "^7.2.0",
+ "eslint-visitor-keys": "^3.4.1",
+ "espree": "^9.5.2",
+ "esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
"find-up": "^5.0.0",
- "glob-parent": "^6.0.1",
- "globals": "^13.15.0",
- "globby": "^11.1.0",
- "grapheme-splitter": "^1.0.4",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
- "js-sdsl": "^4.1.4",
+ "is-path-inside": "^3.0.3",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
@@ -11860,7 +11869,6 @@
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
"optionator": "^0.9.1",
- "regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
@@ -12094,53 +12102,36 @@
"requires": {}
},
"eslint-scope": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
- "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
+ "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
"dev": true,
"requires": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
}
},
- "eslint-utils": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
- "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
- "dev": true,
- "requires": {
- "eslint-visitor-keys": "^2.0.0"
- },
- "dependencies": {
- "eslint-visitor-keys": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
- "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
- "dev": true
- }
- }
- },
"eslint-visitor-keys": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
- "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
+ "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==",
"dev": true
},
"espree": {
- "version": "9.4.0",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
- "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==",
+ "version": "9.5.2",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz",
+ "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==",
"dev": true,
"requires": {
"acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.3.0"
+ "eslint-visitor-keys": "^3.4.1"
}
},
"esquery": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
- "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
"dev": true,
"requires": {
"estraverse": "^5.1.0"
@@ -12441,9 +12432,9 @@
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
},
"globals": {
- "version": "13.17.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
- "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
+ "version": "13.20.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+ "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
"dev": true,
"requires": {
"type-fest": "^0.20.2"
@@ -12495,10 +12486,10 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
- "grapheme-splitter": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
"dev": true
},
"has": {
@@ -12724,6 +12715,12 @@
"has-tostringtag": "^1.0.0"
}
},
+ "is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true
+ },
"is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
@@ -12803,12 +12800,6 @@
"integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
"dev": true
},
- "js-sdsl": {
- "version": "4.1.4",
- "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz",
- "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==",
- "dev": true
- },
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -13533,9 +13524,9 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"punycode": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
- "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"dev": true
},
"queue-microtask": {
@@ -13767,12 +13758,6 @@
"functions-have-names": "^1.2.2"
}
},
- "regexpp": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
- "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
- "dev": true
- },
"regexpu-core": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz",
diff --git a/front/package.json b/front/package.json
index f39cbba5..c5b09c2d 100644
--- a/front/package.json
+++ b/front/package.json
@@ -52,7 +52,7 @@
"@types/styled-components": "^5.1.26",
"@types/uuid": "^9.0.0",
"autoprefixer": "^10.4.14",
- "eslint": "8.23.1",
+ "eslint": "^8.43.0",
"eslint-config-next": "^13.0.6",
"postcss": "^8.4.23",
"tailwindcss": "^3.3.2",
diff --git a/front/services/courses/courses.ts b/front/services/courses/courses.ts
index 62e29b3f..3cf1f13c 100644
--- a/front/services/courses/courses.ts
+++ b/front/services/courses/courses.ts
@@ -9,7 +9,6 @@ import { RequestBody, RequestBodyForm, RequestBodyWithAuthHeader, errorHandling
export async function getOrgCourses(org_id: number, next: any) {
const result: any = await fetch(`${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, RequestBody("GET", null, next));
const res = await errorHandling(result);
-
return res;
}
diff --git a/front/services/utils/ts/requests.ts b/front/services/utils/ts/requests.ts
index e5b9bf5a..b0b49390 100644
--- a/front/services/utils/ts/requests.ts
+++ b/front/services/utils/ts/requests.ts
@@ -1,6 +1,6 @@
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context";
import { denyAccessToUser } from "../react/middlewares/views";
-import { LEARNHOUSE_DOMAIN, LEARNHOUSE_HTTP_PROTOCOL } from "@services/config/config";
+import { getUriWithOrg, LEARNHOUSE_DOMAIN, LEARNHOUSE_HTTP_PROTOCOL } from "@services/config/config";
export const RequestBody = (method: string, data: any, next: any) => {
let HeadersConfig = new Headers({ "Content-Type": "application/json" });
@@ -78,15 +78,16 @@ export const swrFetcher = async (url: string, body: any, router?: AppRouterInsta
export const errorHandling = (res: any) => {
if (!res.ok) {
- const error: any = new Error(`${res.status}: ${res.statusText}`, {});
+ const error: any = new Error(`${res.statusText}`);
error.status = res.status;
throw error;
}
return res.json();
};
-export const revalidateTags = (tags: string[]) => {
+export const revalidateTags = (tags: string[], orgslug: string) => {
+ const url = getUriWithOrg(orgslug, "");
tags.forEach((tag) => {
- fetch(`${LEARNHOUSE_HTTP_PROTOCOL}${LEARNHOUSE_DOMAIN}/api/revalidate?tag=${tag}`);
+ fetch(`${url}/api/revalidate?tag=${tag}`);
});
};
diff --git a/src/main.py b/src/main.py
deleted file mode 100644
index add8842f..00000000
--- a/src/main.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from fastapi import APIRouter
-from src.routers import blocks, trail, users, auth, orgs, roles
-from src.routers.courses import chapters, collections, courses,activities
-
-
-global_router = APIRouter(prefix="/api")
-
-
-# API Routes
-global_router.include_router(users.router, prefix="/users", tags=["users"])
-global_router.include_router(auth.router, prefix="/auth", tags=["auth"])
-global_router.include_router(orgs.router, prefix="/orgs", tags=["orgs"])
-global_router.include_router(roles.router, prefix="/roles", tags=["roles"])
-global_router.include_router(blocks.router, prefix="/blocks", tags=["blocks"])
-global_router.include_router(courses.router, prefix="/courses", tags=["courses"])
-global_router.include_router(chapters.router, prefix="/chapters", tags=["chapters"])
-global_router.include_router(activities.router, prefix="/activities", tags=["activities"])
-global_router.include_router(collections.router, prefix="/collections", tags=["collections"])
-global_router.include_router(trail.router, prefix="/trail", tags=["trail"])
-
-
diff --git a/src/router.py b/src/router.py
new file mode 100644
index 00000000..66f8b633
--- /dev/null
+++ b/src/router.py
@@ -0,0 +1,25 @@
+from fastapi import APIRouter, Depends
+from src.routers import blocks, dev, trail, users, auth, orgs, roles
+from src.routers.courses import chapters, collections, courses, activities
+from src.services.dev.dev import isDevModeEnabled
+
+
+v1_router = APIRouter(prefix="/api/v1")
+
+
+# API Routes
+v1_router.include_router(users.router, prefix="/users", tags=["users"])
+v1_router.include_router(auth.router, prefix="/auth", tags=["auth"])
+v1_router.include_router(orgs.router, prefix="/orgs", tags=["orgs"])
+v1_router.include_router(roles.router, prefix="/roles", tags=["roles"])
+v1_router.include_router(blocks.router, prefix="/blocks", tags=["blocks"])
+v1_router.include_router(courses.router, prefix="/courses", tags=["courses"])
+v1_router.include_router(chapters.router, prefix="/chapters", tags=["chapters"])
+v1_router.include_router(activities.router, prefix="/activities", tags=["activities"])
+v1_router.include_router( collections.router, prefix="/collections", tags=["collections"])
+v1_router.include_router(trail.router, prefix="/trail", tags=["trail"])
+
+# Dev Routes
+v1_router.include_router(
+ dev.router, prefix="/dev", tags=["dev"], dependencies=[Depends(isDevModeEnabled)]
+)
diff --git a/src/routers/auth.py b/src/routers/auth.py
index 0af18380..2cc82973 100644
--- a/src/routers/auth.py
+++ b/src/routers/auth.py
@@ -1,5 +1,6 @@
from fastapi import Depends, APIRouter, HTTPException, Response, status, Request
from fastapi.security import OAuth2PasswordRequestForm
+from config.config import get_learnhouse_config
from src.security.auth import AuthJWT, authenticate_user
from src.services.users.users import PublicUser
@@ -41,7 +42,7 @@ async def login(
refresh_token = Authorize.create_refresh_token(subject=form_data.username)
Authorize.set_refresh_cookies(refresh_token)
# set cookies using fastapi
- response.set_cookie(key="access_token_cookie", value=access_token, httponly=False)
+ response.set_cookie(key="access_token_cookie", value=access_token, httponly=False, domain=get_learnhouse_config().hosting_config.cookie_config.domain)
user = PublicUser(**user.dict())
result = {
diff --git a/src/routers/courses/courses.py b/src/routers/courses/courses.py
index bb6252e4..4aed4c99 100644
--- a/src/routers/courses/courses.py
+++ b/src/routers/courses/courses.py
@@ -1,7 +1,7 @@
from fastapi import APIRouter, Depends, UploadFile, Form, Request
from src.security.auth import get_current_user
-from src.services.courses.courses import Course, create_course, get_course, get_course_meta, get_courses, get_courses_orgslug, update_course, delete_course, update_course_thumbnail
+from src.services.courses.courses import Course, create_course, get_course, get_course_meta, get_courses_orgslug, update_course, delete_course, update_course_thumbnail
from src.services.users.users import PublicUser
@@ -41,21 +41,12 @@ async def api_get_course_meta(request: Request, course_id: str, current_user: P
"""
return await get_course_meta(request, course_id, current_user=current_user)
-
-@router.get("/org_id/{org_id}/page/{page}/limit/{limit}")
-async def api_get_course_by(request: Request, page: int, limit: int, org_id: str):
- """
- Get houses by page and limit
- """
- return await get_courses(request, page, limit, org_id)
-
-
@router.get("/org_slug/{org_slug}/page/{page}/limit/{limit}")
-async def api_get_course_by_orgslug(request: Request, page: int, limit: int, org_slug: str):
+async def api_get_course_by_orgslug(request: Request, page: int, limit: int, org_slug: str, current_user: PublicUser = Depends(get_current_user)):
"""
Get houses by page and limit
"""
- return await get_courses_orgslug(request, page, limit, org_slug)
+ return await get_courses_orgslug(request, current_user, page, limit, org_slug)
@router.put("/{course_id}")
diff --git a/src/routers/dev.py b/src/routers/dev.py
new file mode 100644
index 00000000..318154e8
--- /dev/null
+++ b/src/routers/dev.py
@@ -0,0 +1,18 @@
+from fastapi import APIRouter, Request
+from config.config import get_learnhouse_config
+from src.services.dev.mocks.initial import create_initial_data
+
+
+router = APIRouter()
+
+
+@router.get("/config")
+async def config():
+ config = get_learnhouse_config()
+ return config.dict()
+
+
+@router.get("/mock/initial")
+async def initial_data(request: Request):
+ await create_initial_data(request)
+ return {"Message": "Initial data created 🤖"}
diff --git a/src/security/auth.py b/src/security/auth.py
index a24ee2c8..32309774 100644
--- a/src/security/auth.py
+++ b/src/security/auth.py
@@ -4,6 +4,7 @@ from fastapi import Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from datetime import datetime, timedelta
+from src.services.dev.dev import isDevModeEnabled
from src.services.users.schemas.users import AnonymousUser, PublicUser
from src.services.users.users import security_get_user, security_verify_password
from src.security.security import ALGORITHM, SECRET_KEY
@@ -14,10 +15,10 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login")
#### JWT Auth ####################################################
class Settings(BaseModel):
- authjwt_secret_key: str = "secret"
+ authjwt_secret_key: str = "secret" if isDevModeEnabled() else SECRET_KEY
authjwt_token_location = {"cookies", "headers"}
authjwt_cookie_csrf_protect = False
- authjwt_access_token_expires = False # (pre-alpha only) # TODO: set to 1 hour
+ authjwt_access_token_expires = False if isDevModeEnabled() else 3600
authjwt_cookie_samesite = "lax"
authjwt_cookie_secure = True
authjwt_cookie_domain = get_learnhouse_config().hosting_config.cookie_config.domain
diff --git a/src/security/security.py b/src/security/security.py
index f6384b09..b5918389 100644
--- a/src/security/security.py
+++ b/src/security/security.py
@@ -1,6 +1,7 @@
from fastapi import HTTPException, status, Request
from passlib.context import CryptContext
from passlib.hash import pbkdf2_sha256
+from config.config import get_learnhouse_config
from src.services.roles.schemas.roles import RoleInDB
from src.services.users.schemas.users import UserInDB, UserRolesInOrganization
@@ -10,7 +11,7 @@ from src.services.users.schemas.users import UserInDB, UserRolesInOrganization
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
ACCESS_TOKEN_EXPIRE_MINUTES = 30
-SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
+SECRET_KEY = get_learnhouse_config().security_config.auth_jwt_secret_key
ALGORITHM = "HS256"
### 🔒 JWT ##############################################################
@@ -18,6 +19,7 @@ ALGORITHM = "HS256"
### 🔒 Passwords Hashing ##############################################################
+
async def security_hash_password(password: str):
return pbkdf2_sha256.hash(password)
@@ -25,63 +27,64 @@ async def security_hash_password(password: str):
async def security_verify_password(plain_password: str, hashed_password: str):
return pbkdf2_sha256.verify(plain_password, hashed_password)
+
### 🔒 Passwords Hashing ##############################################################
### 🔒 Roles checking ##############################################################
-async def verify_user_rights_with_roles(request: Request, action: str, user_id: str, element_id: str, element_org_id: str):
+async def verify_user_rights_with_roles(
+ request: Request, action: str, user_id: str, element_id: str, element_org_id: str
+):
"""
Check if the user has the right to perform the action on the element
"""
- roles = request.app.db["roles"]
+ request.app.db["roles"]
users = request.app.db["users"]
user = await users.find_one({"user_id": user_id})
- # Check if user is available
+ #########
+ # Users existence verification
+ #########
+
if not user and user_id != "anonymous":
raise HTTPException(
- status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
-
+ status_code=status.HTTP_404_NOT_FOUND, detail="User rights : User not found"
+ )
+
# Check if user is anonymous
if user_id == "anonymous":
return False
- # Check if the user is an admin
+ # Get User
user: UserInDB = UserInDB(**await users.find_one({"user_id": user_id}))
- # Organization roles verification
+ #########
+ # Organization Roles verification
+ #########
+
for org in user.orgs:
- # TODO: Check if the org_id (user) is the same as the org_id (element)
-
if org.org_id == element_org_id:
- return True
+ # Check if user is owner or reader of the organization
+ if org.org_role == ("owner" or "editor"):
+ return True
- # Check if user is owner or reader of the organization
- if org.org_role == ("owner" or "editor"):
- return True
-
- # If the user is not an owner or a editor, check if he has a role
- # Get user roles
+ #########
+ # Roles verification
+ #########
user_roles = user.roles
- # TODO: Check if the org_id of the role is the same as the org_id of the element using find
-
if action != "create":
- await check_user_role_org_with_element_org(request, element_id, user_roles)
-
- # Check if user has the right role
-
- element_type = await check_element_type(element_id)
- for role_id in user_roles:
- role = RoleInDB(**await roles.find_one({"role_id": role_id}))
- if role.elements[element_type][f"action_{action}"]:
- return True
+ return await check_user_role_org_with_element_org(
+ request, element_id, user_roles, action
+ )
# If no role is found, raise an error
raise HTTPException(
- status_code=status.HTTP_403_FORBIDDEN, detail="You don't have the right to perform this action")
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="User rights : You don't have the right to perform this action",
+ )
async def check_element_type(element_id):
@@ -104,11 +107,17 @@ async def check_element_type(element_id):
return "activities"
else:
raise HTTPException(
- status_code=status.HTTP_409_CONFLICT, detail="Issue verifying element nature")
+ status_code=status.HTTP_409_CONFLICT,
+ detail="User rights : Issue verifying element nature",
+ )
-async def check_user_role_org_with_element_org(request: Request, element_id: str, roles_list: list[UserRolesInOrganization]):
-
+async def check_user_role_org_with_element_org(
+ request: Request,
+ element_id: str,
+ roles_list: list[UserRolesInOrganization],
+ action: str,
+):
element_type = await check_element_type(element_id)
element = request.app.db[element_type]
roles = request.app.db["roles"]
@@ -117,19 +126,26 @@ async def check_user_role_org_with_element_org(request: Request, element_id: str
singular_form_element = element_type[:-1]
element_type_id = singular_form_element + "_id"
-
+
element_org = await element.find_one({element_type_id: element_id})
-
- for role_id in roles_list:
- role = RoleInDB(**await roles.find_one({"role_id": role_id}))
- if role.org_id == element_org["org_id"]:
- return True
- if role.org_id == "*":
- return True
+ for role in roles_list:
+ # Check if The role belongs to the same organization as the element
+ role_db = await roles.find_one({"role_id": role.role_id})
+ role = RoleInDB(**role_db)
+ if role.org_id == element_org["org_id"] or role.org_id == "*":
+ # Check if user has the right role
+ for role in roles_list:
+ role_db = await roles.find_one({"role_id": role.role_id})
+ role = RoleInDB(**role_db)
+ if role.elements[element_type][f"action_{action}"]:
+ return True
else:
raise HTTPException(
- status_code=status.HTTP_403_FORBIDDEN, detail="You don't have the right to perform this action")
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="User rights (roles) : You don't have the right to perform this action",
+ )
+
### 🔒 Roles checking ##############################################################
diff --git a/src/services/courses/activities/activities.py b/src/services/courses/activities/activities.py
index 8816461a..e04bf2f9 100644
--- a/src/services/courses/activities/activities.py
+++ b/src/services/courses/activities/activities.py
@@ -84,6 +84,11 @@ async def get_activity(request: Request, activity_id: str, current_user: PublicU
course = await courses.find_one({"chapters": coursechapter_id})
isCoursePublic = course["public"]
+ isAuthor = current_user.user_id in course["authors"]
+
+ if isAuthor:
+ activity = ActivityInDB(**activity)
+ return activity
# verify course rights
hasRoleRights = await verify_user_rights_with_roles(
diff --git a/src/services/courses/courses.py b/src/services/courses/courses.py
index bd6f2786..aa79f77d 100644
--- a/src/services/courses/courses.py
+++ b/src/services/courses/courses.py
@@ -313,31 +313,15 @@ async def delete_course(request: Request, course_id: str, current_user: PublicUs
####################################################
-async def get_courses(
- request: Request, page: int = 1, limit: int = 10, org_id: str | None = None
-):
- courses = request.app.db["courses"]
- # TODO : Get only courses that user is admin/has roles of
- # get all courses from database
- all_courses = (
- courses.find({"org_id": org_id})
- .sort("name", 1)
- .skip(10 * (page - 1))
- .limit(limit)
- )
-
- return [
- json.loads(json.dumps(course, default=str))
- for course in await all_courses.to_list(length=100)
- ]
-
-
async def get_courses_orgslug(
- request: Request, page: int = 1, limit: int = 10, org_slug: str | None = None
+ request: Request,
+ current_user: PublicUser,
+ page: int = 1,
+ limit: int = 10,
+ org_slug: str | None = None,
):
courses = request.app.db["courses"]
orgs = request.app.db["organizations"]
- # TODO : Get only courses that user is admin/has roles of
# get org_id from slug
org = await orgs.find_one({"slug": org_slug})
@@ -347,13 +331,21 @@ async def get_courses_orgslug(
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist"
)
- # get all courses from database
- all_courses = (
- courses.find({"org_id": org["org_id"]})
- .sort("name", 1)
- .skip(10 * (page - 1))
- .limit(limit)
- )
+ # show only public courses if user is not logged in
+ if current_user.user_id == "anonymous":
+ all_courses = (
+ courses.find({"org_id": org["org_id"], "public": True})
+ .sort("name", 1)
+ .skip(10 * (page - 1))
+ .limit(limit)
+ )
+ else:
+ all_courses = (
+ courses.find({"org_id": org["org_id"]})
+ .sort("name", 1)
+ .skip(10 * (page - 1))
+ .limit(limit)
+ )
return [
json.loads(json.dumps(course, default=str))
@@ -374,6 +366,11 @@ async def verify_rights(
course = await courses.find_one({"course_id": course_id})
+ isAuthor = current_user.user_id in course["authors"]
+
+ if isAuthor:
+ return True
+
if (
current_user.user_id == "anonymous"
and course["public"] is True
@@ -390,7 +387,6 @@ async def verify_rights(
hasRoleRights = await verify_user_rights_with_roles(
request, action, current_user.user_id, course_id, course["org_id"]
)
- isAuthor = current_user.user_id in course["authors"]
if not hasRoleRights and not isAuthor:
raise HTTPException(
diff --git a/src/services/dev/__init__.py b/src/services/dev/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/services/dev/dev.py b/src/services/dev/dev.py
new file mode 100644
index 00000000..6c1e1879
--- /dev/null
+++ b/src/services/dev/dev.py
@@ -0,0 +1,14 @@
+from fastapi import HTTPException
+from config.config import get_learnhouse_config
+
+
+def isDevModeEnabled():
+ config = get_learnhouse_config()
+ if config.general_config.development_mode:
+ return True
+ else:
+ raise HTTPException(
+ status_code=403,
+ detail="Development mode is not enabled",
+ )
+
diff --git a/src/services/dev/mocks/__init__.py b/src/services/dev/mocks/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/services/mocks/initial.py b/src/services/dev/mocks/initial.py
similarity index 99%
rename from src/services/mocks/initial.py
rename to src/services/dev/mocks/initial.py
index 70bdaa7c..6c2da907 100644
--- a/src/services/mocks/initial.py
+++ b/src/services/dev/mocks/initial.py
@@ -124,7 +124,7 @@ async def create_initial_data(request: Request):
await database_orgs.delete_many({})
organizations = []
- for i in range(0, 5):
+ for i in range(0, 2):
company = fake.company()
# remove whitespace and special characters and make lowercase
slug = ''.join(e for e in company if e.isalnum()).lower()