Merge branch 'develop' into future

This commit is contained in:
dakkar 2024-05-31 11:43:09 +01:00
commit 7b630b48b5
6 changed files with 88 additions and 17 deletions

View file

@ -19,6 +19,8 @@ testCommit:
- pnpm install --frozen-lockfile - pnpm install --frozen-lockfile
- pnpm run build - pnpm run build
- pnpm run migrate - pnpm run migrate
- pnpm run --filter=backend lint
- pnpm run --filter=frontend eslint
cache: cache:
key: test key: test
policy: pull-push policy: pull-push

View file

@ -34,7 +34,7 @@
- **ActivityPub support**\ - **ActivityPub support**\
Not on Sharkey? No problem! Not only can Sharkey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed! Not on Sharkey? No problem! Not only can Sharkey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed!
- **Federated Backgrounds and Music status**\ - **Federated Backgrounds and Music status**\
You can add a background to your profile as well as a music status via ListenBrainz, show everyone what music you are currently listening too You can add a background to your profile as well as a music status via ListenBrainz, show everyone what music you are currently listening to
- **Mastodon API**\ - **Mastodon API**\
Sharkey implements the Mastodon API unlike normal Misskey Sharkey implements the Mastodon API unlike normal Misskey
- **UI/UX Improvements**\ - **UI/UX Improvements**\

View file

@ -327,11 +327,11 @@ function applyEnvOverrides(config: Source) {
// these inner functions recurse through the config structure, using // these inner functions recurse through the config structure, using
// the given steps, building the env variable name // the given steps, building the env variable name
function _apply_top(steps: (string | number)[]) { function _apply_top(steps: (string | string[] | number | number[])[]) {
_walk('', [], steps); _walk('', [], steps);
} }
function _walk(name: string, path: (string | number)[], steps: (string | number)[]) { function _walk(name: string, path: (string | number)[], steps: (string | string[] | number | number[])[]) {
// are there more steps after this one? recurse // are there more steps after this one? recurse
if (steps.length > 1) { if (steps.length > 1) {
const thisStep = steps.shift(); const thisStep = steps.shift();
@ -368,7 +368,7 @@ function applyEnvOverrides(config: Source) {
} }
// this recurses down, bailing out if there's no config to override // this recurses down, bailing out if there's no config to override
function _descend(name: string, path: (string | number)[], thisStep: string | number, steps: (string | number)[]) { function _descend(name: string, path: (string | number)[], thisStep: string | number, steps: (string | string[] | number | number[])[]) {
name = `${name}${_step2name(thisStep)}_`; name = `${name}${_step2name(thisStep)}_`;
path = [ ...path, thisStep ]; path = [ ...path, thisStep ];
_walk(name, path, steps); _walk(name, path, steps);
@ -390,10 +390,10 @@ function applyEnvOverrides(config: Source) {
} }
} }
const alwaysStrings = { 'chmodSocket': 1 }; const alwaysStrings = { 'chmodSocket': true } as { [key: string]: boolean };
function _assign(path: (string | number)[], lastStep: string | number, value: string) { function _assign(path: (string | number)[], lastStep: string | number, value: string) {
let thisConfig = config; let thisConfig = config as any;
for (const step of path) { for (const step of path) {
if (!thisConfig[step]) { if (!thisConfig[step]) {
thisConfig[step] = {}; thisConfig[step] = {};
@ -403,9 +403,11 @@ function applyEnvOverrides(config: Source) {
if (!alwaysStrings[lastStep]) { if (!alwaysStrings[lastStep]) {
if (value.match(/^[0-9]+$/)) { if (value.match(/^[0-9]+$/)) {
value = parseInt(value); thisConfig[lastStep] = parseInt(value);
return;
} else if (value.match(/^(true|false)$/i)) { } else if (value.match(/^(true|false)$/i)) {
value = !!value.match(/^true$/i); thisConfig[lastStep] = !!value.match(/^true$/i);
return;
} }
} }
@ -416,7 +418,7 @@ function applyEnvOverrides(config: Source) {
_apply_top([['url', 'port', 'socket', 'chmodSocket', 'disableHsts']]); _apply_top([['url', 'port', 'socket', 'chmodSocket', 'disableHsts']]);
_apply_top(['db', ['host', 'port', 'db', 'user', 'pass']]); _apply_top(['db', ['host', 'port', 'db', 'user', 'pass']]);
_apply_top(['dbSlaves', config.dbSlaves?.keys(), ['host', 'port', 'db', 'user', 'pass']]); _apply_top(['dbSlaves', Array.from((config.dbSlaves ?? []).keys()), ['host', 'port', 'db', 'user', 'pass']]);
_apply_top([ _apply_top([
['redis', 'redisForPubsub', 'redisForJobQueue', 'redisForTimelines'], ['redis', 'redisForPubsub', 'redisForJobQueue', 'redisForTimelines'],
['host','port','username','pass','db','prefix'], ['host','port','username','pass','db','prefix'],

View file

@ -4,16 +4,24 @@
*/ */
import type { IObject } from '../type.js'; import type { IObject } from '../type.js';
function getHrefFrom(one: IObject|string): string | undefined {
if (typeof(one) === 'string') return one;
return one.href;
}
export function assertActivityMatchesUrls(activity: IObject, urls: string[]) { export function assertActivityMatchesUrls(activity: IObject, urls: string[]) {
const idOk = activity.id !== undefined && urls.includes(activity.id); const idOk = activity.id !== undefined && urls.includes(activity.id);
if (idOk) return;
// technically `activity.url` could be an `ApObject = IObject | const url = activity.url;
// string | (IObject | string)[]`, but if it's a complicated thing if (url) {
// and the `activity.id` doesn't match, I think we're fine // `activity.url` can be an `ApObject = IObject | string | (IObject
// rejecting the activity // | string)[]`, we have to look inside it
const urlOk = typeof(activity.url) === 'string' && urls.includes(activity.url); const activityUrls = Array.isArray(url) ? url.map(getHrefFrom) : [getHrefFrom(url)];
const goodUrl = activityUrls.find(u => u && urls.includes(u));
if (!idOk && !urlOk) { if (goodUrl) return;
throw new Error(`bad Activity: neither id(${activity?.id}) nor url(${activity?.url}) match location(${urls})`);
} }
throw new Error(`bad Activity: neither id(${activity?.id}) nor url(${JSON.stringify(activity?.url)}) match location(${urls})`);
} }

View file

@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: dakkar and sharkey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { IObject } from '@/core/activitypub/type.js';
import { describe, expect, test } from '@jest/globals';
import { assertActivityMatchesUrls } from '@/core/activitypub/misc/check-against-url.js';
function assertOne(activity: IObject) {
// return a function so we can use `.toThrow`
return () => assertActivityMatchesUrls(activity, ['good']);
}
describe('assertActivityMatchesUrls', () => {
test('id', () => {
expect(assertOne({ id: 'bad' })).toThrow(/bad Activity/);
expect(assertOne({ id: 'good' })).not.toThrow();
});
test('simple url', () => {
expect(assertOne({ url: 'bad' })).toThrow(/bad Activity/);
expect(assertOne({ url: 'good' })).not.toThrow();
});
test('array of urls', () => {
expect(assertOne({ url: ['bad'] })).toThrow(/bad Activity/);
expect(assertOne({ url: ['bad', 'other'] })).toThrow(/bad Activity/);
expect(assertOne({ url: ['good'] })).not.toThrow();
expect(assertOne({ url: ['bad', 'good'] })).not.toThrow();
});
test('array of objects', () => {
expect(assertOne({ url: [{ href: 'bad' }] })).toThrow(/bad Activity/);
expect(assertOne({ url: [{ href: 'bad' }, { href: 'other' }] })).toThrow(/bad Activity/);
expect(assertOne({ url: [{ href: 'good' }] })).not.toThrow();
expect(assertOne({ url: [{ href: 'bad' }, { href: 'good' }] })).not.toThrow();
});
test('mixed array', () => {
expect(assertOne({ url: [{ href: 'bad' }, 'other'] })).toThrow(/bad Activity/);
expect(assertOne({ url: [{ href: 'bad' }, 'good'] })).not.toThrow();
expect(assertOne({ url: ['bad', { href: 'good' }] })).not.toThrow();
});
test('id and url', () => {
expect(assertOne({ id: 'other', url: 'bad' })).toThrow(/bad Activity/);
expect(assertOne({ id: 'bad', url: 'good' })).not.toThrow();
expect(assertOne({ id: 'good', url: 'bad' })).not.toThrow();
});
});

View file

@ -296,6 +296,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import { miLocalStorage } from '@/local-storage.js'; import { miLocalStorage } from '@/local-storage.js';
import { globalEvents } from '@/events.js'; import { globalEvents } from '@/events.js';
import { claimAchievement } from '@/scripts/achievements.js'; import { claimAchievement } from '@/scripts/achievements.js';
import { deepMerge } from '@/scripts/merge.js';
const lang = ref(miLocalStorage.getItem('lang')); const lang = ref(miLocalStorage.getItem('lang'));
const fontSize = ref(miLocalStorage.getItem('fontSize')); const fontSize = ref(miLocalStorage.getItem('fontSize'));
@ -322,7 +323,14 @@ const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDi
const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction')); const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction'));
const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes'));
const clickToOpen = computed(defaultStore.makeGetterSetter('clickToOpen')); const clickToOpen = computed(defaultStore.makeGetterSetter('clickToOpen'));
const showBots = computed(defaultStore.makeGetterSetter('tlWithBots')); // copied from src/pages/timeline.vue
const showBots = computed<boolean>({
get: () => defaultStore.reactiveState.tl.value.filter.withBots,
set: (newValue) => {
const out = deepMerge({ filter: { withBots: newValue } }, defaultStore.state.tl);
defaultStore.set('tl', out);
},
});
const collapseFiles = computed(defaultStore.makeGetterSetter('collapseFiles')); const collapseFiles = computed(defaultStore.makeGetterSetter('collapseFiles'));
const autoloadConversation = computed(defaultStore.makeGetterSetter('autoloadConversation')); const autoloadConversation = computed(defaultStore.makeGetterSetter('autoloadConversation'));
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));