refactor: break up ap/index.js

Now it is more obvious that I only handle the homepage from the library
and have the login as an implementation detail.

Once I figure out how to talk to Vervis' API I can drop Cheerio and use
ReST instead.

Signed-off-by: André Jaenisch <andre.jaenisch@posteo.de>
This commit is contained in:
André Jaenisch 2024-06-14 18:31:53 +02:00
parent eca61c975e
commit bafd831696
No known key found for this signature in database
GPG key ID: 5A668E771F1ED854
3 changed files with 151 additions and 115 deletions

View file

@ -0,0 +1,87 @@
/* Private functions dealing with parsing the homepage of a Vervis instance.
* Copyright (C) 2024 André Jaenisch
* SPDX-FileCopyrightText: 2024 André Jaenisch
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { AxiosHeaders } from 'axios';
import * as cheerio from 'cheerio';
import { getRemoteLogin, postRemoteLogin } from './login.js';
export async function getHomepage({ account, passphrase, server }) {
const vervis =
{
1: 'https://fig.fr33domlover.site',
2: 'https://grape.fr33domlover.site',
3: 'https://walnut.fr33domlover.site'
}[server] || null;
const loginFormData = await getRemoteLogin(vervis);
const loggedInResponse = await postRemoteLogin({
username: account,
password: passphrase,
loginFormData
});
if (loggedInResponse.status === 200) {
const headers = new AxiosHeaders();
headers.set({ Cookie: loggedInResponse.headers['set-cookie'].join(';') });
const response = await loginFormData.instance.get('/', {
headers
});
const patches = [];
const projects = [];
const repos = [];
const teams = [];
const tickets = [];
const dom = cheerio.load(response.data);
if (dom('h2:contains("Your teams") + p').text().includes("aren't a member")) {
console.log('No teams');
}
if (dom('h2:contains("Your repos") + ul').text() === '') {
console.log('No repos');
}
if (dom('h2:contains("Your ticket trackers") + ul').text() === '') {
console.log('No ticket trackers');
}
if (dom('h2:contains("Your patch trackers") + ul').text() === '') {
console.log('No patch trackers');
}
if (dom('h2:contains("Your projects") + ul').text() === '') {
console.log('No projects');
} else {
dom('h2:contains("Your projects") + ul li a')
.get()
.map((a) => {
projects.push(dom(a).text());
});
}
return {
cookies: response.headers['set-cookie'].join(';'),
patches,
projects,
repos,
teams,
tickets,
username: account,
vervis
};
}
return null;
}

View file

@ -16,6 +16,7 @@ import axios, { AxiosHeaders } from 'axios';
import * as cheerio from 'cheerio';
import pkg from '../../../../package.json';
import { getHomepage } from './homepage.js';
const loginResponse = {
id: '87bcb6de-bb70-11ee-b719-6756da82e80f',
@ -68,118 +69,3 @@ export const requests = {
return Promise.reject(new Error('Invalid Login'));
}
};
async function getRemoteLogin(vervis) {
const httpsAgent = new Agent({ rejectUnauthorized: false });
const headers = new AxiosHeaders();
headers.setUserAgent(`Anvil/${pkg.version}`);
const instance = axios.create({
baseURL: vervis,
headers,
httpsAgent,
withCredentials: true
});
const response = await instance.get('/auth/login');
const dom = cheerio.load(response.data);
const form = dom('form');
return {
instance,
action: form.attr('action'),
csrf: dom('form input[name="_token"]').attr('value'),
enctype: form.attr('enctype'),
method: form.attr('method'),
cookies: response.headers['set-cookie']
};
}
async function postRemoteLogin({ username, password, loginFormData }) {
const headers = new AxiosHeaders();
headers.setContentType(loginFormData.enctype);
headers.set({ Cookie: loginFormData.cookies.join(';') });
const data = {
_token: loginFormData.csrf,
f1: username,
f2: password
};
return loginFormData.instance({
method: loginFormData.method,
url: loginFormData.action,
headers,
data
});
}
async function getHomepage({ account, passphrase, server }) {
const vervis =
{
1: 'https://fig.fr33domlover.site',
2: 'https://grape.fr33domlover.site',
3: 'https://walnut.fr33domlover.site'
}[server] || null;
const loginFormData = await getRemoteLogin(vervis);
const loggedInResponse = await postRemoteLogin({
username: account,
password: passphrase,
loginFormData
});
if (loggedInResponse.status === 200) {
const headers = new AxiosHeaders();
headers.set({ Cookie: loggedInResponse.headers['set-cookie'].join(';') });
const response = await loginFormData.instance.get('/', {
headers
});
const patches = [];
const projects = [];
const repos = [];
const teams = [];
const tickets = [];
const dom = cheerio.load(response.data);
if (dom('h2:contains("Your teams") + p').text().includes("aren't a member")) {
console.log('No teams');
}
if (dom('h2:contains("Your repos") + ul').text() === '') {
console.log('No repos');
}
if (dom('h2:contains("Your ticket trackers") + ul').text() === '') {
console.log('No ticket trackers');
}
if (dom('h2:contains("Your patch trackers") + ul').text() === '') {
console.log('No patch trackers');
}
if (dom('h2:contains("Your projects") + ul').text() === '') {
console.log('No projects');
} else {
dom('h2:contains("Your projects") + ul li a')
.get()
.map((a) => {
projects.push(dom(a).text());
});
}
return {
cookies: response.headers['set-cookie'].join(';'),
patches,
projects,
repos,
teams,
tickets,
username: account,
vervis
};
}
return null;
}

View file

@ -0,0 +1,63 @@
/* Private functions dealing with the login into a Vervis instance.
* Copyright (C) 2024 André Jaenisch
* SPDX-FileCopyrightText: 2024 André Jaenisch
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { Agent } from 'node:https';
import axios, { AxiosHeaders } from 'axios';
import * as cheerio from 'cheerio';
import pkg from '../../../../package.json';
export async function getRemoteLogin(vervis) {
const httpsAgent = new Agent({ rejectUnauthorized: false });
const headers = new AxiosHeaders();
headers.setUserAgent(`Anvil/${pkg.version}`);
const instance = axios.create({
baseURL: vervis,
headers,
httpsAgent,
withCredentials: true
});
const response = await instance.get('/auth/login');
const dom = cheerio.load(response.data);
const form = dom('form');
return {
instance,
action: form.attr('action'),
csrf: dom('form input[name="_token"]').attr('value'),
enctype: form.attr('enctype'),
method: form.attr('method'),
cookies: response.headers['set-cookie']
};
}
export async function postRemoteLogin({ username, password, loginFormData }) {
const headers = new AxiosHeaders();
headers.setContentType(loginFormData.enctype);
headers.set({ Cookie: loginFormData.cookies.join(';') });
const data = {
_token: loginFormData.csrf,
f1: username,
f2: password
};
return loginFormData.instance({
method: loginFormData.method,
url: loginFormData.action,
headers,
data
});
}