This commit is contained in:
Morten Olsen
2021-08-17 23:27:18 +02:00
commit a181db8c49
13 changed files with 2956 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/node_modules
/*.log
/.env

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# Aula Client [WIP]
VERY unofficial NodeJS client for www.aula.dk

5
jest.config.js Normal file
View File

@@ -0,0 +1,5 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

22
package.json Normal file
View 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
View 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
View File

@@ -0,0 +1,5 @@
import AulaClient from './Client';
export {
AulaClient,
};

9
src/types/Image.ts Normal file
View File

@@ -0,0 +1,9 @@
interface Image {
id: number,
key: string;
bucket: string;
isImageScalingPending: boolean;
url: string;
}
export default Image;

View 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
View 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
View 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
View 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
View 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"
]
}

2737
yarn.lock Normal file

File diff suppressed because it is too large Load Diff