From e5f3085a5937bfecf5ea7a1b28dcf54482960186 Mon Sep 17 00:00:00 2001 From: flipsimon <28535045+flipsimon@users.noreply.github.com> Date: Tue, 27 Feb 2024 19:20:15 +0100 Subject: [PATCH] WIP setting up peertube client, creating channel and uploading video in test. not yet working: communication with local activity pub server --- dev/peertube/docker-compose.yml | 29 +-- packages/repco-activitypub/src/server.ts | 2 + .../repco-activitypub/src/util/peertube.ts | 169 +++++++++++++++++- .../util/peertube-upload-test.js | 8 + packages/repco-core/package.json | 3 +- packages/repco-core/test/activitypub.ts | 145 ++++++++------- packages/repco-core/test/util/setup.ts | 55 ++++++ yarn.lock | 2 +- 8 files changed, 329 insertions(+), 84 deletions(-) create mode 100644 packages/repco-activitypub/util/peertube-upload-test.js diff --git a/dev/peertube/docker-compose.yml b/dev/peertube/docker-compose.yml index 415b170e..297cbd10 100644 --- a/dev/peertube/docker-compose.yml +++ b/dev/peertube/docker-compose.yml @@ -10,18 +10,19 @@ services: image: chocobozzz/peertube:production-bullseye env_file: - .env - ports: # - "1935:1935" # Comment if you don't want to use the live feature - - "9000:9000" # Uncomment if you use another webserver/proxy or test PeerTube in local, otherwise not suitable for production + - "${PT_PORT:-9000}:9000" # Uncomment if you use another webserver/proxy or test PeerTube in local, otherwise not suitable for production volumes: - # - ./docker-volume/assets:/app/client/dist - - ./docker-volume/data:/data - - ./docker-volume/config:/config + # - ${VOLUME_ROOT:-./docker-volume}/assets:/app/client/dist + - ${VOLUME_ROOT:-./docker-volume}/data:/data + - ${VOLUME_ROOT:-./docker-volume}/config:/config depends_on: - postgres - redis # - postfix + environment: + - PEERTUBE_WEBSERVER_PORT=${PT_PORT:-9000} restart: "always" extra_hosts: - "host.docker.internal:host-gateway" @@ -31,20 +32,20 @@ services: env_file: - .env volumes: - - ./docker-volume/db:/var/lib/postgresql/data + - ${VOLUME_ROOT:-./docker-volume}/db:/var/lib/postgresql/data restart: "always" redis: image: redis:6-alpine volumes: - - ./docker-volume/redis:/data + - ${VOLUME_ROOT:-./docker-volume}/redis:/data restart: "always" - # postfix: - # image: mwader/postfix-relay - # env_file: - # - .env - # volumes: - # - ./docker-volume/opendkim/keys:/etc/opendkim/keys - # restart: "always" + postfix: + image: mwader/postfix-relay + env_file: + - .env + volumes: + - ${VOLUME_ROOT:-./docker-volume}/opendkim/keys:/etc/opendkim/keys + restart: "always" diff --git a/packages/repco-activitypub/src/server.ts b/packages/repco-activitypub/src/server.ts index 0d32cf04..7ce730a5 100644 --- a/packages/repco-activitypub/src/server.ts +++ b/packages/repco-activitypub/src/server.ts @@ -5,8 +5,10 @@ import express from 'express' import { createLogger } from 'repco-common' import { ActivityPub } from './ap.js' import { ApiError } from './error.js' +import { PeertubeClient } from './util/peertube.js' export { ActivityPub } from './ap.js' +export { PeertubeClient } from './util/peertube.js' export const logger = createLogger('ap') diff --git a/packages/repco-activitypub/src/util/peertube.ts b/packages/repco-activitypub/src/util/peertube.ts index d51abef5..42bf5151 100644 --- a/packages/repco-activitypub/src/util/peertube.ts +++ b/packages/repco-activitypub/src/util/peertube.ts @@ -1,4 +1,4 @@ -import { fetch, Headers, RequestInit } from 'undici' +import { fetch, Headers, RequestInit, FormData } from 'undici' import { FetchError } from '../ap/fetch.js' export interface ClientToken { @@ -48,13 +48,45 @@ export class PeertubeClient { } } - async login() { + async createChannel(name: string) { + const data = { name, displayName: name } + const res = await this.fetch('/video-channels', { method: 'POST', data }) + console.log("RES", res) + return (res as any).videoChannel.id as number + } + + async uploadVideo(channelId: number, name: string) { + const path = '/videos/upload' + const url = this.url + '/api/v1' + path + const body = new FormData() + body.set("channelId", "" + channelId) + body.set("name", name) + body.set("videofile", new Blob([testVideo()]), "cbalogo.mp4") + body.set("privacy", 1) + const headers = new Headers() + if (this.token) { + headers.set('authorization', 'Bearer ' + this.token.access_token) + } + // headers.set('content-type', 'multipart/form-data') + const res = await fetch(url, { body, headers, method: 'POST' }) + if (!res.ok) { + throw await FetchError.fromResponse(res) + } + if (res.status === 204) return undefined + try { + const text = await res.text() + return JSON.parse(text) + } catch (err) { + throw await FetchError.fromResponse(res, err as Error) + } + } + + async login(username = process.env.PEERTUBE_USERNAME, password = process.env.PEERTUBE_PASSWORD) { const clientTokens = await this.fetch('/oauth-clients/local') - const { PEERTUBE_USERNAME, PEERTUBE_PASSWORD } = process.env - if (!PEERTUBE_PASSWORD) { + if (!password) { throw new Error('Missing PEERTUBE_PASSWORD environment variable') } - if (!PEERTUBE_USERNAME) { + if (!username) { throw new Error('Missing PEERTUBE_USERNAME environment variable') } const params = new URLSearchParams({ @@ -62,8 +94,8 @@ export class PeertubeClient { client_secret: clientTokens.client_secret, grant_type: 'password', response_type: 'code', - username: PEERTUBE_USERNAME, - password: PEERTUBE_PASSWORD, + username, + password, }) this.token = await this.fetch('/users/token', { method: 'POST', @@ -71,3 +103,126 @@ export class PeertubeClient { }) } } + +function testVideo() { + return Buffer.from(` +AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAFd5tZGF0AAACrgYF//+q +3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE1NSByMjkxNyAwYTg0ZDk4IC0gSC4yNjQvTVBF +Ry00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAxOCAtIGh0dHA6Ly93d3cudmlkZW9sYW4u +b3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFs +eXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVk +X3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBk +ZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTcg +bG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRl +cmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJf +cHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9 +MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3Jl +ZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAu +NjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAR +W2WIhAA7//73Tr8Cm1TCKgOSVwr2yqQmWblSawHypgAA80pZob+CwfCZe8Apyin1xCfFigwqpXF0 +mULQrd4zCMIh1MMOp7VsjFLkivKbtZw3uix7Us/DKo1Nq8Glog9zGOPyAlRNbI0AybEiKlcT83sv +9/S0UDs5fneX+kGp7shLPkaZUEcf5D3Tyc7iv5PUh3ISAJrHOhlZlVOKUUfI/AQcHKKuCzsSUe1G +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 +`.replace('\ņ', ''), 'base64') +} \ No newline at end of file diff --git a/packages/repco-activitypub/util/peertube-upload-test.js b/packages/repco-activitypub/util/peertube-upload-test.js new file mode 100644 index 00000000..784a4c78 --- /dev/null +++ b/packages/repco-activitypub/util/peertube-upload-test.js @@ -0,0 +1,8 @@ +import Repl from 'repl' +import { PeertubeClient } from '../dist/src/util/peertube.js' + +const pt = new PeertubeClient("http://host.docker.internal:9000") +await pt.login("root", "peertube") +const channelId = await pt.createChannel("foochannel1") +await pt.uploadVideo(channelId, "testvideo2") + diff --git a/packages/repco-core/package.json b/packages/repco-core/package.json index ad0a3441..558f7405 100644 --- a/packages/repco-core/package.json +++ b/packages/repco-core/package.json @@ -27,8 +27,8 @@ "iso8601-duration": "^2.1.1", "level": "^8.0.0", "multiformats": "^11.0.2", - "repco-common": "*", "repco-activitypub": "*", + "repco-common": "*", "repco-prisma": "*", "rss-parser": "^3.12.0", "speedometer": "^1.1.0", @@ -44,6 +44,7 @@ "@types/tempy": "^0.3.0", "brittle": "^2.4.0", "esbuild": "^0.14.51", + "express": "^4.18.2", "get-port": "^6.1.2", "nanobench-utils": "^1.0.1", "split2": "^4.2.0", diff --git a/packages/repco-core/test/activitypub.ts b/packages/repco-core/test/activitypub.ts index 7aab13d8..62899093 100644 --- a/packages/repco-core/test/activitypub.ts +++ b/packages/repco-core/test/activitypub.ts @@ -1,12 +1,15 @@ import test from 'brittle' import { fileURLToPath } from 'node:url' -import { ActivityPub, setGlobalApInstance } from 'repco-activitypub' +import { ActivityPub, setGlobalApInstance, PeertubeClient, mountActivityPub } from 'repco-activitypub' import { assertFixture, mockFetch } from './util/fetch.js' -import { setup } from './util/setup.js' +import { setup, setupPeertube } from './util/setup.js' import { repoRegistry } from '../lib.js' import { ingestUpdatesFromDataSources } from '../src/datasource.js' import { ActivityPubDataSourcePlugin } from '../src/datasources/activitypub.js' import { DataSourcePluginRegistry } from '../src/plugins.js' +import express from 'express' +import getPort from 'get-port' + // Path to fixtures. Resolves to repco-core/test/fixtures/datasource-activitypub/$name const fixturePath = (name: string) => @@ -17,62 +20,82 @@ const fixturePath = (name: string) => ), ) -// test('peertube datasource - basic1', async (assert) => { -// mockFetch(assert, fixturePath('basic1')) -// const prisma = await setup(assert) -// const baseUrl = process.env.REPCO_URL || 'http://localhost:8765' -// const ap = new ActivityPub(prisma, baseUrl + '/ap') -// setGlobalApInstance(ap) -// const repo = await repoRegistry.create(prisma, 'test') -// const plugins = new DataSourcePluginRegistry() -// const activityPubPlugin = new ActivityPubDataSourcePlugin() -// plugins.register(activityPubPlugin) -// await repo.dsr.create( -// repo.prisma, -// plugins, -// activityPubPlugin.definition.uid, -// { -// user: 'cryptix_channel', -// domain: 'peertube.1312.media', -// repo: repo.name, -// }, -// repo.did, -// ) -// await ingestUpdatesFromDataSources(repo) -// // TODO: Provide mocking capability to uids -// const entities = await prisma.contentItem.findMany({ -// select: { -// // uid: true, -// title: true, -// content: true, -// pubDate: true, -// PrimaryGrouping: { -// select: { -// // uid: true, -// title: true, -// // uri: true, -// }, -// }, -// Concepts: { -// select: { -// // uid: true, -// name: true, -// }, -// }, -// MediaAssets: { -// select: { -// // uid: true, -// mediaType: true, -// title: true, -// Files: { -// select: { -// // uid: true, -// contentUrl: true, -// }, -// }, -// }, -// }, -// }, -// }) -// await assertFixture(assert, fixturePath('entities.json'), entities) -// }) +test('peertube datasource - basic1', async (assert) => { + mockFetch(assert, fixturePath('basic1')) + const prisma = await setup(assert) + const { teardown, peertubeUrl } = await setupPeertube() + console.log("PEERTUBE_URL: ", peertubeUrl) + assert.teardown(teardown, { order: 0 }) + + const pt = new PeertubeClient(peertubeUrl) + await pt.login("root", "peertube") + const channelId = await pt.createChannel("testchannel") + await pt.uploadVideo(channelId, "testvideo") + + const apServerPort = await getPort() + const apUrl = `http://host.docker.internal:${apServerPort}/ap` + const ap = new ActivityPub(prisma, apUrl) + const server = express() + mountActivityPub(server, ap, { prefix: '/ap' }) + await new Promise((resolve, reject) => { + server.once('error', reject) + server.listen(apServerPort, () => { + console.log(`!!!!!!!!!!!!!!!!!!!!!!! activitypub server listening on ${apUrl}`) + resolve(true); + }) + }) + setGlobalApInstance(ap) + + const repo = await repoRegistry.create(prisma, 'test') + const plugins = new DataSourcePluginRegistry() + const activityPubPlugin = new ActivityPubDataSourcePlugin() + plugins.register(activityPubPlugin) + await repo.dsr.create( + repo.prisma, + plugins, + activityPubPlugin.definition.uid, + { + user: 'testchannel', + domain: peertubeUrl, + repo: repo.name, + }, + repo.did, + ) + await ingestUpdatesFromDataSources(repo) + // TODO: Provide mocking capability to uids + const entities = await prisma.contentItem.findMany({ + select: { + // uid: true, + title: true, + content: true, + pubDate: true, + PrimaryGrouping: { + select: { + // uid: true, + title: true, + // uri: true, + }, + }, + Concepts: { + select: { + // uid: true, + name: true, + }, + }, + MediaAssets: { + select: { + // uid: true, + mediaType: true, + title: true, + Files: { + select: { + // uid: true, + contentUrl: true, + }, + }, + }, + }, + }, + }) + await assertFixture(assert, fixturePath('entities.json'), entities) +}) diff --git a/packages/repco-core/test/util/setup.ts b/packages/repco-core/test/util/setup.ts index bcf5e9b1..be76b6ed 100644 --- a/packages/repco-core/test/util/setup.ts +++ b/packages/repco-core/test/util/setup.ts @@ -2,6 +2,9 @@ import 'source-map-support/register.js' import getPort from 'get-port' import p from 'path' import split2 from 'split2' +import fs from 'fs/promises' +import { temporaryDirectory } from 'tempy' + import { Test } from 'brittle' import { ChildProcess, @@ -48,6 +51,58 @@ export async function setup2(test: Test) { return [await setup(test), await setup(test)] } +export async function setupPeertube(log: LogFn = console.log) { + // create tempdir. + const volumeDir = temporaryDirectory({ prefix: 'repco-peertube-test' }) + const port = await getPort() + const composeFile = p.join(REPCO_ROOT, 'dev/peertube/docker-compose.yml') + const projectName = p.basename(volumeDir) + const args = ['compose', '-p', projectName, '-f', composeFile, 'up', '--force-recreate'] + const env = { + PT_PORT: `${port}`, + VOLUME_ROOT: volumeDir + } + // const container0 = spawn('docker', args, { log, env }) + // // TODO: find out if there's a better way and/or file bug report in peertube... + // await new Promise(resolve => setTimeout(resolve, 2000)) + // const args2 = ['compose', '-p', projectName, '-f', composeFile, 'exec', 'peertube', 'sed', '-i', "s/dnsCache: true,/dnsCache: false,/g", '/app/dist/server/helpers/requests.js'] + // await spawn('docker', args2) + // const args3 = ['compose', '-p', projectName, '-f', composeFile, 'down'] + // await spawn('docker', args3) + const args4 = ['compose', '-p', projectName, '-f', composeFile, 'up'] + const container = spawn('docker', args4, { log, env }) + + + // store the PID for cleanup on exit + if (container.child.pid) dockerPids.push(container.child.pid) + const ready = waitForLines(container.child.stdout!, [ + /HTTP server listening on/, + ]) + + container.child.stdout!.pipe(split2()).on('data', line => log('peertube: ' + line)) + + // wait until the container dies or is ready to accept connections + await Promise.race([container, ready]) + // TODO: remove + await new Promise(resolve => setTimeout(resolve, 5000)) + + + const peertubeUrl =`http://host.docker.internal:${port}` + const teardown = async () => { + container.child.kill() + log("waiting for peertube to shutdown...") + try { + await container + const args = ['compose', '-p', projectName, '-f', composeFile, 'rm', '-f', '-s'] + await spawn('docker', args) + } catch (_err) {} + await fs.rmdir(volumeDir, { recursive: true }) + + // await spawn('docker', ['stop', '-t', '0', name]) + } + return { teardown, peertubeUrl } +} + // Run a postgres container and import migrations. export async function setupDb(log: LogFn = console.log) { const port = await getPort() diff --git a/yarn.lock b/yarn.lock index 7c554dd0..c29e12ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7185,7 +7185,7 @@ express-async-errors@^3.1.1: resolved "https://registry.yarnpkg.com/express-async-errors/-/express-async-errors-3.1.1.tgz#6053236d61d21ddef4892d6bd1d736889fc9da41" integrity sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng== -express@^4.17.1, express@^4.18.1: +express@^4.17.1, express@^4.18.1, express@^4.18.2: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==