mirror of
https://github.com/morten-olsen/aula-client.git
synced 2026-02-08 01:06:25 +01:00
init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/node_modules
|
||||||
|
/*.log
|
||||||
|
/.env
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Aula Client [WIP]
|
||||||
|
|
||||||
|
VERY unofficial NodeJS client for www.aula.dk
|
||||||
5
jest.config.js
Normal file
5
jest.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
};
|
||||||
22
package.json
Normal file
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "aula",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"axios-cookiejar-support": "^1.0.1",
|
||||||
|
"cheerio": "^1.0.0-rc.10",
|
||||||
|
"qs": "^6.10.1",
|
||||||
|
"tough-cookie": "^4.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^27.0.1",
|
||||||
|
"@types/qs": "^6.9.7",
|
||||||
|
"@types/tough-cookie": "^4.0.1",
|
||||||
|
"dotenv": "^10.0.0",
|
||||||
|
"jest": "^27.0.6",
|
||||||
|
"ts-jest": "^27.0.5",
|
||||||
|
"typescript": "^4.3.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
95
src/Client.ts
Normal file
95
src/Client.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import cookieSupport from 'axios-cookiejar-support';
|
||||||
|
import cheerio from 'cheerio';
|
||||||
|
import tough from 'tough-cookie';
|
||||||
|
import qs from 'qs';
|
||||||
|
|
||||||
|
cookieSupport(axios);
|
||||||
|
|
||||||
|
class AulaClient {
|
||||||
|
#cookies: tough.CookieJar;
|
||||||
|
#username: string;
|
||||||
|
#password: string;
|
||||||
|
#profiles?: any[] = [];
|
||||||
|
#context?: any;
|
||||||
|
|
||||||
|
constructor(username: string, password: string) {
|
||||||
|
this.#cookies = new tough.CookieJar();
|
||||||
|
this.#username = username;
|
||||||
|
this.#password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
#createLoginCookie = async () => {
|
||||||
|
const profiles = await this.request({
|
||||||
|
method: 'profiles.getProfilesByLogin'
|
||||||
|
});
|
||||||
|
const context = await this.request({
|
||||||
|
method: 'profiles.getProfileContext',
|
||||||
|
portalrole: 'guardian',
|
||||||
|
});
|
||||||
|
this.#profiles = profiles.data.profiles;
|
||||||
|
this.#context = context.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
get loggedIn() {
|
||||||
|
return !!this.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
get profiles() {
|
||||||
|
return this.#profiles!;
|
||||||
|
}
|
||||||
|
|
||||||
|
get context() {
|
||||||
|
return this.#context!;
|
||||||
|
}
|
||||||
|
|
||||||
|
get childIds() {
|
||||||
|
return this.profiles[0].children.map((c: any) => c.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
login = async () => {
|
||||||
|
let response = await axios.get('https://login.aula.dk/auth/login.php?type=unilogin', {
|
||||||
|
jar: this.#cookies,
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
if (response.config.url === 'https://www.aula.dk:443/portal/') {
|
||||||
|
await this.#createLoginCookie();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const page = cheerio.load(response.data);
|
||||||
|
const [form] = page('form');
|
||||||
|
if (!form) {
|
||||||
|
throw new Error('Form not found');
|
||||||
|
}
|
||||||
|
const { action } = form.attribs;
|
||||||
|
const inputs = page('form input');
|
||||||
|
const postData: {[name: string]: string} = {};
|
||||||
|
for (let input of inputs) {
|
||||||
|
const { name, value } = input.attribs;
|
||||||
|
if (name === 'username') {
|
||||||
|
postData.username = this.#username;
|
||||||
|
} else if (name === 'password') {
|
||||||
|
postData.password = this.#password;
|
||||||
|
} else {
|
||||||
|
postData[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = await axios.post(action, qs.stringify(postData), {
|
||||||
|
jar: this.#cookies,
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new Error('Login took to many rounds');
|
||||||
|
}
|
||||||
|
|
||||||
|
public request = async <T = any>(query: any) => {
|
||||||
|
const { data } = await axios.get<Response<T>>(`https://www.aula.dk/api/v11?${qs.stringify(query)}`, {
|
||||||
|
jar: this.#cookies,
|
||||||
|
withCredentials: true,
|
||||||
|
})
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AulaClient;
|
||||||
5
src/index.ts
Normal file
5
src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import AulaClient from './Client';
|
||||||
|
|
||||||
|
export {
|
||||||
|
AulaClient,
|
||||||
|
};
|
||||||
9
src/types/Image.ts
Normal file
9
src/types/Image.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
interface Image {
|
||||||
|
id: number,
|
||||||
|
key: string;
|
||||||
|
bucket: string;
|
||||||
|
isImageScalingPending: boolean;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Image;
|
||||||
17
src/types/InstitutionProfile.ts
Normal file
17
src/types/InstitutionProfile.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Image from './Image';
|
||||||
|
|
||||||
|
interface InstitutionProfile {
|
||||||
|
profileId: number,
|
||||||
|
id: number,
|
||||||
|
institutionCode: string;
|
||||||
|
institutionName: string;
|
||||||
|
role: 'child' | string;
|
||||||
|
name: string;
|
||||||
|
profilePicture: Image;
|
||||||
|
mainGroup: null;
|
||||||
|
shortName: string;
|
||||||
|
insitutionRolw: 'daycare' | string;
|
||||||
|
metadata: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InstitutionProfile;
|
||||||
15
src/types/Profile.ts
Normal file
15
src/types/Profile.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import InstitutionProfile from './InstitutionProfile';
|
||||||
|
|
||||||
|
interface Profile {
|
||||||
|
institutionProfiles: InstitutionProfile[];
|
||||||
|
children: any[];
|
||||||
|
age18AndOlder: null;
|
||||||
|
overConsentAge: null;
|
||||||
|
contactInfoEditable: null;
|
||||||
|
portalRole: 'guardian' | string;
|
||||||
|
isLatestDataPolicyAccepted: boolean;
|
||||||
|
profileId: number;
|
||||||
|
displayName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Profile;
|
||||||
10
src/types/Response.ts
Normal file
10
src/types/Response.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
interface Response<T = any> {
|
||||||
|
status: {
|
||||||
|
code: number;
|
||||||
|
message: 'OK' | string;
|
||||||
|
};
|
||||||
|
data: T;
|
||||||
|
version: number;
|
||||||
|
module: string;
|
||||||
|
method: string;
|
||||||
|
}
|
||||||
18
tests/login.test.ts
Normal file
18
tests/login.test.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
import { AulaClient } from '../src';
|
||||||
|
|
||||||
|
describe('login', () => {
|
||||||
|
let client: AulaClient;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
client = new AulaClient(process.env.USERNAME!, process.env.PASSWORD!);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to log in', async () => {
|
||||||
|
await client.login();
|
||||||
|
await client.request({
|
||||||
|
method: 'presence.getDailyOverview',
|
||||||
|
childIds: client.childIds,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2018",
|
||||||
|
"module": "commonjs",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"forceConsistentCasingInFileNames": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user