From 311e5123e77b3f330f09c825d4145dcaed717581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Jaenisch?= Date: Fri, 14 Jun 2024 20:53:32 +0200 Subject: [PATCH] feat: query Vervis for information on a person MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .gitignore | 1 + src/lib/components/templates/Profile.svelte | 18 +++++ src/lib/server/ap/homepage.js | 80 ++++++++++++++++++++- src/lib/server/ap/login.js | 2 - src/routes/account/login/+page.server.js | 10 ++- src/routes/profile/+page.server.js | 13 ++++ 6 files changed, 118 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 21e162a..26220c9 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/src/lib/components/templates/Profile.svelte b/src/lib/components/templates/Profile.svelte index 5657f09..b0c4daf 100644 --- a/src/lib/components/templates/Profile.svelte +++ b/src/lib/components/templates/Profile.svelte @@ -96,6 +96,24 @@ You should have received a copy of the GNU Affero General Public License along w } })}

+
+

The following is DEBUG information and will be removed in the near future.

+ {#if data?.user} +
+ Person +
{JSON.stringify(data.user.person, null, 2)}
+
+
+ Followings +
{JSON.stringify(data.user.followings, null, 2)}
+
+
+ Followings mapped +

These are the contents of the items in the previous dump.

+
{JSON.stringify(data.user.followingsMap, null, 2)}
+
+ {/if} +
diff --git a/src/lib/server/ap/homepage.js b/src/lib/server/ap/homepage.js index 75ad395..c17a78a 100644 --- a/src/lib/server/ap/homepage.js +++ b/src/lib/server/ap/homepage.js @@ -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); +} diff --git a/src/lib/server/ap/login.js b/src/lib/server/ap/login.js index 423c8b9..3d80efc 100644 --- a/src/lib/server/ap/login.js +++ b/src/lib/server/ap/login.js @@ -59,5 +59,3 @@ export async function postRemoteLogin({ username, password, loginFormData }) { data }); } - - diff --git a/src/routes/account/login/+page.server.js b/src/routes/account/login/+page.server.js index 2031123..53f9772 100644 --- a/src/routes/account/login/+page.server.js +++ b/src/routes/account/login/+page.server.js @@ -10,6 +10,8 @@ * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +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 }); diff --git a/src/routes/profile/+page.server.js b/src/routes/profile/+page.server.js index 77d3df2..9691176 100644 --- a/src/routes/profile/+page.server.js +++ b/src/routes/profile/+page.server.js @@ -9,12 +9,14 @@ * * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +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 {}; + } +}