Hotfix: remove channelfed changes
This commit is contained in:
parent
ead8a4eb3b
commit
dc1c095f92
11 changed files with 14 additions and 184 deletions
packages/backend
migration
src
db
misc
models
remote/activitypub/renderer
server
|
@ -1,27 +0,0 @@
|
||||||
import { genRsaKeyPair } from '../built/misc/gen-key-pair.js';
|
|
||||||
|
|
||||||
export class channelActors1653237040103 {
|
|
||||||
name = 'channelActors1653237040103'
|
|
||||||
|
|
||||||
async up(queryRunner) {
|
|
||||||
await queryRunner.query(`CREATE TABLE "channel_keypair" ("channelId" character varying(32) NOT NULL, "publicKey" character varying(4096) NOT NULL, "privateKey" character varying(4096) NOT NULL, CONSTRAINT "REL_7be19c4d7a902ff9fd664ca9ae" UNIQUE ("channelId"), CONSTRAINT "PK_f771c2d8b4078218f64014c7cb" PRIMARY KEY ("channelId"))`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "channel" ADD COLUMN "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[]`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "channel" ADD COLUMN "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[]`);
|
|
||||||
|
|
||||||
const channels = await queryRunner.query(`SELECT id FROM "channel"`);
|
|
||||||
|
|
||||||
for (let i = 0; i < channels.length; i++) {
|
|
||||||
let channelId = channels[i].id;
|
|
||||||
console.log(channelId);
|
|
||||||
const keypair = await genRsaKeyPair(4096);
|
|
||||||
|
|
||||||
await queryRunner.query(`INSERT INTO "channel_keypair" ("publicKey", "privateKey", "channelId") VALUES ('${keypair.publicKey}', '${keypair.privateKey}', '${channelId}')`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async down(queryRunner) {
|
|
||||||
await queryRunner.query(`DROP TABLE "channel_keypair"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "emojis"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "tags"`);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -64,7 +64,6 @@ import { MutedNote } from '@/models/entities/muted-note.js';
|
||||||
import { Channel } from '@/models/entities/channel.js';
|
import { Channel } from '@/models/entities/channel.js';
|
||||||
import { ChannelFollowing } from '@/models/entities/channel-following.js';
|
import { ChannelFollowing } from '@/models/entities/channel-following.js';
|
||||||
import { ChannelNotePining } from '@/models/entities/channel-note-pining.js';
|
import { ChannelNotePining } from '@/models/entities/channel-note-pining.js';
|
||||||
import { ChannelKeypair } from '@/models/entities/channel-keypair.js';
|
|
||||||
import { RegistryItem } from '@/models/entities/registry-item.js';
|
import { RegistryItem } from '@/models/entities/registry-item.js';
|
||||||
import { Ad } from '@/models/entities/ad.js';
|
import { Ad } from '@/models/entities/ad.js';
|
||||||
import { PasswordResetRequest } from '@/models/entities/password-reset-request.js';
|
import { PasswordResetRequest } from '@/models/entities/password-reset-request.js';
|
||||||
|
@ -170,7 +169,6 @@ export const entities = [
|
||||||
Channel,
|
Channel,
|
||||||
ChannelFollowing,
|
ChannelFollowing,
|
||||||
ChannelNotePining,
|
ChannelNotePining,
|
||||||
ChannelKeypair,
|
|
||||||
RegistryItem,
|
RegistryItem,
|
||||||
Ad,
|
Ad,
|
||||||
PasswordResetRequest,
|
PasswordResetRequest,
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
import { UserKeypairs, ChannelKeypairs } from '@/models/index.js';
|
import { UserKeypairs } from '@/models/index.js';
|
||||||
import { User } from '@/models/entities/user.js';
|
import { User } from '@/models/entities/user.js';
|
||||||
import { Channel } from '@/models/entities/channel.js';
|
|
||||||
import { UserKeypair } from '@/models/entities/user-keypair.js';
|
import { UserKeypair } from '@/models/entities/user-keypair.js';
|
||||||
import { ChannelKeypair } from '@/models/entities/channel-keypair.js';
|
|
||||||
import { Cache } from './cache.js';
|
import { Cache } from './cache.js';
|
||||||
|
|
||||||
const userCache = new Cache<UserKeypair>(Infinity);
|
const cache = new Cache<UserKeypair>(Infinity);
|
||||||
const channelCache = new Cache<ChannelKeypair>(Infinity);
|
|
||||||
|
|
||||||
export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
|
export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
|
||||||
return await cache.fetch(userId, () => UserKeypairs.findOneByOrFail({ userId }));
|
return await cache.fetch(userId, () => UserKeypairs.findOneByOrFail({ userId: userId }));
|
||||||
}
|
|
||||||
|
|
||||||
export async function getChannelKeypair(channelId: Channel['id']): Promise<ChannelKeypair> {
|
|
||||||
return await channelCache.fetch(channelId, () => ChannelKeypairs.findOneByOrFail({ channelId }));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm';
|
|
||||||
import { Channel } from './channel.js';
|
|
||||||
import { id } from '../id.js';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export class ChannelKeypair {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public channelId: Channel['id'];
|
|
||||||
|
|
||||||
@OneToOne(type => Channel, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public channel: Channel | null;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 4096,
|
|
||||||
})
|
|
||||||
public publicKey: string;
|
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 4096,
|
|
||||||
})
|
|
||||||
public privateKey: string;
|
|
||||||
|
|
||||||
constructor(data: Partial<ChannelKeypair>) {
|
|
||||||
if (data == null) return;
|
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(data)) {
|
|
||||||
(this as any)[k] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -59,17 +59,6 @@ export class Channel {
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public banner: DriveFile | null;
|
public banner: DriveFile | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 128, array: true, default: '{}',
|
|
||||||
})
|
|
||||||
public emojis: string[];
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column('varchar', {
|
|
||||||
length: 128, array: true, default: '{}',
|
|
||||||
})
|
|
||||||
public tags: string[];
|
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column('integer', {
|
@Column('integer', {
|
||||||
default: 0,
|
default: 0,
|
||||||
|
|
|
@ -59,7 +59,6 @@ import { ChannelRepository } from './repositories/channel.js';
|
||||||
import { MutedNote } from './entities/muted-note.js';
|
import { MutedNote } from './entities/muted-note.js';
|
||||||
import { ChannelFollowing } from './entities/channel-following.js';
|
import { ChannelFollowing } from './entities/channel-following.js';
|
||||||
import { ChannelNotePining } from './entities/channel-note-pining.js';
|
import { ChannelNotePining } from './entities/channel-note-pining.js';
|
||||||
import { ChannelKeypair } from './entities/channel-keypair.js';
|
|
||||||
import { RegistryItem } from './entities/registry-item.js';
|
import { RegistryItem } from './entities/registry-item.js';
|
||||||
import { Ad } from './entities/ad.js';
|
import { Ad } from './entities/ad.js';
|
||||||
import { PasswordResetRequest } from './entities/password-reset-request.js';
|
import { PasswordResetRequest } from './entities/password-reset-request.js';
|
||||||
|
@ -128,7 +127,6 @@ export const MutedNotes = db.getRepository(MutedNote);
|
||||||
export const Channels = (ChannelRepository);
|
export const Channels = (ChannelRepository);
|
||||||
export const ChannelFollowings = db.getRepository(ChannelFollowing);
|
export const ChannelFollowings = db.getRepository(ChannelFollowing);
|
||||||
export const ChannelNotePinings = db.getRepository(ChannelNotePining);
|
export const ChannelNotePinings = db.getRepository(ChannelNotePining);
|
||||||
export const ChannelKeypairs = db.getRepository(ChannelKeypair);
|
|
||||||
export const RegistryItems = db.getRepository(RegistryItem);
|
export const RegistryItems = db.getRepository(RegistryItem);
|
||||||
export const Webhooks = db.getRepository(Webhook);
|
export const Webhooks = db.getRepository(Webhook);
|
||||||
export const Ads = db.getRepository(Ad);
|
export const Ads = db.getRepository(Ad);
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
import { URL } from 'node:url';
|
|
||||||
import * as mfm from 'mfm-js';
|
|
||||||
import renderImage from './image.js';
|
|
||||||
import renderKey from './key.js';
|
|
||||||
import config from '@/config/index.js';
|
|
||||||
import { Channel } from '@/models/entities/channel.js';
|
|
||||||
import { toHtml } from '../../../mfm/to-html.js';
|
|
||||||
import { getEmojis } from './note.js';
|
|
||||||
import renderEmoji from './emoji.js';
|
|
||||||
import renderHashtag from './hashtag.js';
|
|
||||||
import { DriveFiles, UserProfiles } from '@/models/index.js';
|
|
||||||
import { getChannelKeypair } from '@/misc/keypair-store.js';
|
|
||||||
|
|
||||||
export async function renderGroup(channel: Channel) {
|
|
||||||
const id = `${config.url}/channels/${channel.id}`;
|
|
||||||
|
|
||||||
const banner = await channel.bannerId ? DriveFiles.findOneBy({ id: channel.bannerId }) : Promise.resolve(undefined);
|
|
||||||
|
|
||||||
const emojis = await getEmojis(channel.emojis);
|
|
||||||
const apemojis = emojis.map(emoji => renderEmoji(emoji));
|
|
||||||
|
|
||||||
const hashtagTags = (channel.tags || []).map(tag => renderHashtag(tag));
|
|
||||||
|
|
||||||
const tag = [
|
|
||||||
...apemojis,
|
|
||||||
...hashtagTags,
|
|
||||||
];
|
|
||||||
|
|
||||||
const keypair = await getChannelKeypair(channel.id);
|
|
||||||
|
|
||||||
const group = {
|
|
||||||
type: 'Group',
|
|
||||||
id,
|
|
||||||
// TODO: Impliment these actually
|
|
||||||
inbox: `${id}/inbox`,
|
|
||||||
outbox: `${id}/outbox`,
|
|
||||||
followers: `${id}/followers`,
|
|
||||||
following: `${id}/following`, // this is required by spec. cant be empty. sadge
|
|
||||||
sharedInbox: `${config.url}/inbox`,
|
|
||||||
endpoints: { sharedInbox: `${config.url}/inbox` },
|
|
||||||
url: id,
|
|
||||||
name: channel.name,
|
|
||||||
summary: channel.description ? toHtml(mfm.parse(channel.description)) : null,
|
|
||||||
icon: null,
|
|
||||||
image: banner ? renderImage(banner) : null,
|
|
||||||
tag,
|
|
||||||
publicKey: renderKey(channel, keypair, `#main-key`),
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
return group;
|
|
||||||
}
|
|
|
@ -1,10 +1,9 @@
|
||||||
import config from '@/config/index.js';
|
import config from '@/config/index.js';
|
||||||
import { ILocalUser } from '@/models/entities/user.js';
|
import { ILocalUser } from '@/models/entities/user.js';
|
||||||
import { UserKeypair } from '@/models/entities/user-keypair.js';
|
import { UserKeypair } from '@/models/entities/user-keypair.js';
|
||||||
import { ChannelKeypair } from '@/models/entities/channel-keypair.js';
|
|
||||||
import { createPublicKey } from 'node:crypto';
|
import { createPublicKey } from 'node:crypto';
|
||||||
|
|
||||||
export default (user: ILocalUser, key: UserKeypair | ChannelKeypair, postfix?: string) => ({
|
export default (user: ILocalUser, key: UserKeypair, postfix?: string) => ({
|
||||||
id: `${config.url}/users/${user.id}${postfix || '/publickey'}`,
|
id: `${config.url}/users/${user.id}${postfix || '/publickey'}`,
|
||||||
type: 'Key',
|
type: 'Key',
|
||||||
owner: `${config.url}/users/${user.id}`,
|
owner: `${config.url}/users/${user.id}`,
|
||||||
|
|
|
@ -61,8 +61,7 @@ export default async function renderNote(note: Note, dive = true, isTalk = false
|
||||||
let cc: string[] = [];
|
let cc: string[] = [];
|
||||||
|
|
||||||
if (note.channelId) {
|
if (note.channelId) {
|
||||||
const channel = `${config.url}/channels/${note.channelId}`;
|
to = [{ type: 'Group', name: note.channelId }];
|
||||||
to = [channel];
|
|
||||||
cc = [`${attributedTo}/followers`, 'https://www.w3.org/ns/activitystreams#Public'].concat(mentions);
|
cc = [`${attributedTo}/followers`, 'https://www.w3.org/ns/activitystreams#Public'].concat(mentions);
|
||||||
} else {
|
} else {
|
||||||
if (note.visibility === 'public') {
|
if (note.visibility === 'public') {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import httpSignature from '@peertube/http-signature';
|
||||||
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
|
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
|
||||||
import renderNote from '@/remote/activitypub/renderer/note.js';
|
import renderNote from '@/remote/activitypub/renderer/note.js';
|
||||||
import renderKey from '@/remote/activitypub/renderer/key.js';
|
import renderKey from '@/remote/activitypub/renderer/key.js';
|
||||||
import { renderGroup } from '@/remote/activitypub/renderer/group.js';
|
|
||||||
import { renderPerson } from '@/remote/activitypub/renderer/person.js';
|
import { renderPerson } from '@/remote/activitypub/renderer/person.js';
|
||||||
import renderEmoji from '@/remote/activitypub/renderer/emoji.js';
|
import renderEmoji from '@/remote/activitypub/renderer/emoji.js';
|
||||||
import Outbox, { packActivity } from './activitypub/outbox.js';
|
import Outbox, { packActivity } from './activitypub/outbox.js';
|
||||||
|
@ -14,7 +13,7 @@ import Following from './activitypub/following.js';
|
||||||
import Featured from './activitypub/featured.js';
|
import Featured from './activitypub/featured.js';
|
||||||
import { inbox as processInbox } from '@/queue/index.js';
|
import { inbox as processInbox } from '@/queue/index.js';
|
||||||
import { isSelfHost } from '@/misc/convert-host.js';
|
import { isSelfHost } from '@/misc/convert-host.js';
|
||||||
import { Notes, Users, Emojis, NoteReactions, Channels } from '@/models/index.js';
|
import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js';
|
||||||
import { ILocalUser, User } from '@/models/entities/user.js';
|
import { ILocalUser, User } from '@/models/entities/user.js';
|
||||||
import { In, IsNull, Not } from 'typeorm';
|
import { In, IsNull, Not } from 'typeorm';
|
||||||
import { renderLike } from '@/remote/activitypub/renderer/like.js';
|
import { renderLike } from '@/remote/activitypub/renderer/like.js';
|
||||||
|
@ -252,22 +251,4 @@ router.get('/follows/:follower/:followee', async ctx => {
|
||||||
setResponseType(ctx);
|
setResponseType(ctx);
|
||||||
});
|
});
|
||||||
|
|
||||||
// channel
|
|
||||||
router.get('/channels/:channelId', async (ctx, next) => {
|
|
||||||
if (!isActivityPubReq(ctx)) return await next();
|
|
||||||
|
|
||||||
const channel = await Channels.findOneBy({
|
|
||||||
id: ctx.params.channelId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (channel == null) {
|
|
||||||
ctx.status = 404;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.body = renderActivity(await renderGroup(channel));
|
|
||||||
ctx.set('Cache-Control', 'public, max-age=180');
|
|
||||||
setResponseType(ctx);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -2,11 +2,7 @@ import define from '../../define.js';
|
||||||
import { ApiError } from '../../error.js';
|
import { ApiError } from '../../error.js';
|
||||||
import { Channels, DriveFiles } from '@/models/index.js';
|
import { Channels, DriveFiles } from '@/models/index.js';
|
||||||
import { Channel } from '@/models/entities/channel.js';
|
import { Channel } from '@/models/entities/channel.js';
|
||||||
import { ChannelKeypair } from '@/models/entities/channel-keypair.js';
|
|
||||||
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
|
|
||||||
import { genId } from '@/misc/gen-id.js';
|
import { genId } from '@/misc/gen-id.js';
|
||||||
import { db } from '@/db/postgre.js';
|
|
||||||
|
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['channels'],
|
tags: ['channels'],
|
||||||
|
@ -54,26 +50,14 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyPair = await genRsaKeyPair(4096);
|
const channel = await Channels.insert({
|
||||||
|
id: genId(),
|
||||||
let channel!: Channel;
|
createdAt: new Date(),
|
||||||
|
userId: user.id,
|
||||||
await db.transaction(async transactionalEntityManager => {
|
name: ps.name,
|
||||||
channel = await transactionalEntityManager.save(new Channel({
|
description: ps.description || null,
|
||||||
id: genId(),
|
bannerId: banner ? banner.id : null,
|
||||||
createdAt: new Date(),
|
} as Channel).then(x => Channels.findOneByOrFail(x.identifiers[0]));
|
||||||
userId: user.id,
|
|
||||||
name: ps.name,
|
|
||||||
description: ps.description || null,
|
|
||||||
bannerId: banner ? banner.id : null,
|
|
||||||
}));
|
|
||||||
|
|
||||||
await transactionalEntityManager.insert(ChannelKeypair, {
|
|
||||||
publicKey: keyPair.publicKey,
|
|
||||||
privateKey: keyPair.privateKey,
|
|
||||||
channelId: channel.id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return await Channels.pack(channel, user);
|
return await Channels.pack(channel, user);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue