diff --git a/README.md b/README.md index aa482e5..9e8a1f0 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ It returns a boolean that is `true` when the server is a demo. - [x] parser: able to get/build attachment's URL - [x] Find Pronote instances with longitude and latitude - [x] `findPronoteInstances(fetcher, { longitude, latitude })` +- [x] Get information about an instance from its URL + - [x] `getPronoteInstanceInformation(fetcher, { pronoteURL })` - [x] Periods - [x] client: `.periods` (property) - [x] parser: `Period` diff --git a/examples/instance-information.ts b/examples/instance-information.ts new file mode 100644 index 0000000..8f1d756 --- /dev/null +++ b/examples/instance-information.ts @@ -0,0 +1,22 @@ +import { getPronoteInstanceInformation, defaultPawnoteFetcher } from "../src"; + +(async () => { + const instance = await getPronoteInstanceInformation(defaultPawnoteFetcher, { + pronoteURL: "https://demo.index-education.net/pronote" + }); + + console.log("Root URL:", instance.pronoteRootURL); + console.log("School Name:", instance.schoolName); + console.log("Server Version:", instance.version); + + console.group("Available account types:", instance.accounts.length); + instance.accounts.forEach(account => { + console.log("->", account.name, `(${account.id})`); + }) + console.groupEnd(); + + if (instance.entURL) { + console.log("ENT is activated, base URL is", instance.entURL); + console.log("-> ENT token:", instance.entToken!); + } +})(); diff --git a/src/api/geolocation/index.ts b/src/api/geolocation/index.ts index df5c591..26a5d48 100644 --- a/src/api/geolocation/index.ts +++ b/src/api/geolocation/index.ts @@ -27,7 +27,7 @@ export const callApiGeolocation = makeApiHandler(async (fetcher, body: searchParamsBody.toString() }); - let data = await response.json() as PronoteApiGeolocation["response"]; + let data = await response.json(); data = Array.isArray(data) ? data : []; // Restructure the results to be more readable. diff --git a/src/api/index.ts b/src/api/index.ts index 4b32b6a..e8f8e99 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -6,6 +6,7 @@ export { callApiUserHomework } from "./user/homework"; export { callApiUserHomeworkStatus } from "./user/homeworkStatus"; export { callApiUserTimetable } from "./user/timetable"; export { callApiGeolocation } from "./geolocation"; +export { callApiInstance } from "./instance"; export type { ApiLoginInformations, PronoteApiLoginInformations } from "./login/informations/types"; export type { ApiLoginIdentify, PronoteApiLoginIdentify } from "./login/identify/types"; @@ -15,3 +16,4 @@ export type { ApiUserHomework, PronoteApiUserHomework } from "./user/homework/ty export type { ApiUserHomeworkStatus, PronoteApiUserHomeworkStatus } from "./user/homeworkStatus/types"; export type { ApiUserTimetable, PronoteApiUserTimetable } from "./user/timetable/types"; export type { ApiGeolocation, PronoteApiGeolocation } from "./geolocation/types"; +export type { ApiInstance, PronoteApiInstance } from "./instance/types"; diff --git a/src/api/instance/index.ts b/src/api/instance/index.ts new file mode 100644 index 0000000..c6220f7 --- /dev/null +++ b/src/api/instance/index.ts @@ -0,0 +1,50 @@ +import { makeApiHandler } from "~/utils/api"; +import type { PronoteApiInstance, ApiInstance } from "./types"; +import { cleanPronoteUrl } from "~/pronote/url"; +import { PRONOTE_ACCOUNT_TYPES, PronoteApiAccountType } from "~/constants/accounts"; +import { PRONOTE_INSTANCE_MOBILE_INFOS_PATH } from "~/constants/urls"; + +/** + * Filter function to prevent TS issues. + * Allows to check that every item is defined and make them typed to `PronoteApiAccountType`. + */ +const isPronoteApiAccountType = (item: PronoteApiAccountType | undefined): item is PronoteApiAccountType => { + return Boolean(item); +}; + +/** + * Takes an instance URL and return informations about it such as... + * - available account types ; + * - instance name ; + * - base URL and potential ENT URL + */ +export const callApiInstance = makeApiHandler(async (fetcher, input) => { + const pronoteURL = cleanPronoteUrl(input.pronoteURL); + const informationURL = `${pronoteURL}/${PRONOTE_INSTANCE_MOBILE_INFOS_PATH}`; + + const response = await fetcher(informationURL, { + method: "GET" + }); + + // Build the local date to get the timezone offset, right after. + // const local_date = new Date(new Date().toLocaleString("fr-FR", { timeZone: req.body.timezone })); + + const data = await response.json(); + + // Calculate the timezone offset between the server and the client. + // const timezone_offset = local_date.getTime() - new Date(data.date).getTime(); + + // Filter the accounts to only keep the ones that are supported. + const accounts = data.espaces.map((account) => PRONOTE_ACCOUNT_TYPES.find( + (account_type) => account_type.path === account.URL + )).filter(isPronoteApiAccountType); + + return { + accounts, + pronoteRootURL: pronoteURL, + version: data.version[0], + schoolName: data.nomEtab, + entURL: data.CAS.actif ? data.CAS.casURL : undefined, + entToken: data.CAS.actif ? data.CAS.jetonCAS : undefined + }; +}); diff --git a/src/api/instance/types.ts b/src/api/instance/types.ts new file mode 100644 index 0000000..a3bf025 --- /dev/null +++ b/src/api/instance/types.ts @@ -0,0 +1,36 @@ +import { PronoteApiAccountId } from "~/constants/accounts"; + +export interface PronoteApiInstance { + request: Record + + response: { + version: number[] + date: string + CAS: { actif: false } | { actif: true, casURL: string, jetonCAS: string } + espaces: Array<{ nom: string, URL: string }> + nomEtab: string + } +} + +export interface ApiInstance { + input: { + pronoteURL: string + } + + output: { + version: number; + schoolName: string; + + accounts: Array<{ + name: string + id: PronoteApiAccountId + }> + + pronoteRootURL: string + + /** URL of the ENT we have to handle. */ + entURL?: string + /** Used to generate new temporary passwords for Pronote after ENT login. */ + entToken?: string + } +} diff --git a/src/index.ts b/src/index.ts index 07810a4..3d9e445 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,8 +3,8 @@ export { PronoteApiAccountId } from "~/constants/accounts"; export { authenticatePronoteCredentials, authenticateToken, authenticatePronoteQRCode } from "~/authenticate"; export { PronoteApiGradeType } from "./pronote/grades"; -// Geolocation. -export { callApiGeolocation as findPronoteInstances } from "~/api"; +export { callApiGeolocation as findPronoteInstances, callApiInstance as getPronoteInstanceInformation } from "~/api"; + import type { ApiGeolocation } from "~/api"; /**