feat: query Vervis for information on a person

I discovered that Vervis understands Content Negotiation.
That is, once I figure out my peopleId I can navigate to the respective
paths and resceive JSON that is equal to the prettyjson'ed HTML views I
can see in the web browser.

I don't find everything I would like to have. Therefore I'm writing the
results to a file for now and read it in the profile so I can talk to
our designer how to move on with what we have right now.

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

1
.gitignore vendored
View file

@ -18,3 +18,4 @@ storybook-static
# See https://tauri.app/v1/guides/development/development-cycle#2-start-tauri-development-window
src-tauri/target
account.json

View file

@ -96,6 +96,24 @@ You should have received a copy of the GNU Affero General Public License along w
}
})}
</p>
<div>
<p>The following is DEBUG information and will be removed in the near future.</p>
{#if data?.user}
<div>
<b>Person</b>
<pre>{JSON.stringify(data.user.person, null, 2)}</pre>
</div>
<div>
<b>Followings</b>
<pre>{JSON.stringify(data.user.followings, null, 2)}</pre>
</div>
<div>
<b>Followings mapped</b>
<p>These are the contents of the items in the previous dump.</p>
<pre>{JSON.stringify(data.user.followingsMap, null, 2)}</pre>
</div>
{/if}
</div>
</div>
</div>
<!-- History -->

View file

@ -32,7 +32,7 @@ export async function getHomepage({ account, passphrase, server }) {
if (loggedInResponse.status === 200) {
const headers = new AxiosHeaders();
headers.set({ Cookie: loggedInResponse.headers['set-cookie'].join(';') });
headers.set({ Cookie: getCookie(loggedInResponse) });
const response = await loginFormData.instance.get('/', {
headers
@ -45,6 +45,15 @@ export async function getHomepage({ account, passphrase, server }) {
const tickets = [];
const dom = cheerio.load(response.data);
const inbox = getInboxLinkFromResponse(response);
const peopleId = getPeopleIdFromInboxLink(inbox);
const r = await fetchPersonData(loginFormData, getCookie(response), peopleId);
const person = getPersonFromResponse(r);
const s = await fetchFollowings(loginFormData, getCookie(r), person);
const followings = getFollowingsFromResponse(s);
const t = await fetchFollowingsMap(loginFormData, getCookie(s), followings);
const followingsMap = getFollowingsMapFromRsponses(t);
if (dom('h2:contains("Your teams") + p').text().includes("aren't a member")) {
console.log('No teams');
}
@ -72,7 +81,10 @@ export async function getHomepage({ account, passphrase, server }) {
}
return {
cookies: response.headers['set-cookie'].join(';'),
cookies: getCookie(t[t.length - 1]),
followings,
followingsMap,
person,
patches,
projects,
repos,
@ -85,3 +97,67 @@ export async function getHomepage({ account, passphrase, server }) {
return null;
}
function getCookie(lastResponse) {
return lastResponse.headers['set-cookie'].join(';');
}
function getInboxLinkFromResponse(response) {
const dom = cheerio.load(response.data);
// Since the PeopleID is not marked up anywhere on the homepage,
// I pick the first link I know that it contains it.
// If there was a /me endpoint or something that would help
return dom('header span + span + span a').attr('href');
}
function getPersonFromResponse(response) {
return response.data;
}
function getFollowingsFromResponse(response) {
return response.data;
}
function getFollowingsMapFromRsponses(responses) {
return responses.map((response) => response.data);
}
function getPeopleIdFromInboxLink(inboxLink) {
// The id is part of the pathname as /people/:peopleId/inbox
const url = new URL(inboxLink);
const parts = url.pathname.split('/');
return parts[2];
}
async function fetchPersonData(loginFormData, cookie, peopleId) {
const headers = new AxiosHeaders();
headers.set({ Cookie: cookie });
const response = await loginFormData.instance.get(`/people/${peopleId}`, {
headers
});
return response;
}
async function fetchFollowings(loginFormData, cookie, person) {
const headers = new AxiosHeaders();
headers.set({ Cookie: cookie });
const { pathname } = new URL(person.following);
const response = await loginFormData.instance.get(pathname, { headers });
return response;
}
async function fetchFollowingsMap(loginFormData, cookie, followings) {
const headers = new AxiosHeaders();
headers.set({ Cookie: cookie });
const mappedFollowings = followings.items.map((following) => {
const { pathname } = new URL(following);
return loginFormData.instance.get(pathname, { headers });
});
return Promise.all(mappedFollowings);
}

View file

@ -59,5 +59,3 @@ export async function postRemoteLogin({ username, password, loginFormData }) {
data
});
}

View file

@ -10,6 +10,8 @@
* 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 { writeFile } from 'node:fs/promises';
import { fail, redirect } from '@sveltejs/kit';
import { requests } from '$lib/server/ap/index.js';
@ -31,8 +33,12 @@ export const actions = {
}
try {
const response = await requests.post('/login', { account, passphrase, server });
console.log('DEBUG', response);
const response = await requests.post('/login', {
account,
passphrase,
server
});
await writeFile('account.json', JSON.stringify(response));
} catch (exc) {
console.error(exc);
return fail(400, { account, incorrect: true });

View file

@ -9,12 +9,14 @@
*
* 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 { readFile } from 'node:fs/promises';
import { requests } from '$lib/server/ap/index.js';
/** @type {import('./$types').PageServerLoad} */
export async function load({ params }) {
const profile = await requests.get('/profile');
const account = await readAccountData();
return {
server: {
@ -22,9 +24,20 @@ export async function load({ params }) {
},
user: {
...profile,
...account,
created_at: new Date(profile.created_at),
created_with: 'Anvil',
instance: 'example.com'
}
};
}
async function readAccountData() {
try {
const account = await readFile('account.json', 'utf8');
return JSON.parse(account);
} catch (exc) {
console.error(exc);
return {};
}
}