Compare commits

...

7 commits

Author SHA1 Message Date
dakkar
454adc4923 merge: allow overriding all string config values via env - fixes #465 (!476)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/476

Closes #465

Approved-by: Marie <marie@kaifa.ch>
2024-04-03 10:36:52 +00:00
Marie
8c955fcce5 merge: use correct note design in favorites page - fixes #483 (!481)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/481

Closes #483

Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
Approved-by: Marie <marie@kaifa.ch>
2024-04-03 09:28:01 +00:00
dakkar
bd7c4f66f3 use correct note design in favorites page - fixes #483 2024-03-27 16:03:35 +00:00
dakkar
4271402e0d recognise numbers and boolean values 2024-03-24 11:17:55 +00:00
dakkar
0e8cdb30b7 allow setting values not present in the config file
replicas and arrays in general, are more complicated :/
2024-03-24 11:12:17 +00:00
dakkar
435cab01c8 deal with (possible, future) non-alnum config keys 2024-03-21 10:00:16 +00:00
dakkar
25e6409cc9 allow overriding all string config values via env - fixes #465
will need end-user documentation!
2024-03-20 15:38:20 +00:00
3 changed files with 130 additions and 3 deletions

View file

@ -212,6 +212,8 @@ export function loadConfig(): Config {
{} as Source, {} as Source,
) as Source; ) as Source;
applyEnvOverrides(config);
const url = tryCreateUrl(config.url); const url = tryCreateUrl(config.url);
const version = meta.version; const version = meta.version;
const host = url.host; const host = url.host;
@ -304,3 +306,123 @@ function convertRedisOptions(options: RedisOptionsSource, host: string): RedisOp
db: options.db ?? 0, db: options.db ?? 0,
}; };
} }
/*
this function allows overriding any string-valued config option with
a sensible-named environment variable
e.g. `MK_CONFIG_MEILISEARCH_APIKEY` sets `config.meilisearch.apikey`
you can also override a single `dbSlave` value,
e.g. `MK_CONFIG_DBSLAVES_1_PASS` sets the password for the 2nd
database replica (the first one would be
`MK_CONFIG_DBSLAVES_0_PASS`); in this case, `config.dbSlaves` must
be set to an array of the right size already in the file
values can be read from files, too: setting `MK_DB_PASS_FILE` to
`/some/file` would set the main database password to the contents of
`/some/file` (trimmed of whitespaces)
*/
function applyEnvOverrides(config: Source) {
// these inner functions recurse through the config structure, using
// the given steps, building the env variable name
function _apply_top(steps: (string | number)[]) {
_walk('', [], steps);
}
function _walk(name: string, path: (string | number)[], steps: (string | number)[]) {
// are there more steps after this one? recurse
if (steps.length > 1) {
const thisStep = steps.shift();
if (thisStep === null || thisStep === undefined) return;
// if a step is not a simple value, iterate through it
if (typeof thisStep === 'object') {
for (const thisOneStep of thisStep) {
_descend(name, path, thisOneStep, steps);
}
} else {
_descend(name, path, thisStep, steps);
}
// the actual override has happened at the bottom of the
// recursion, we're done
return;
}
// this is the last step, same thing as above
const lastStep = steps[0];
if (typeof lastStep === 'object') {
for (const lastOneStep of lastStep) {
_lastBit(name, path, lastOneStep);
}
} else {
_lastBit(name, path, lastStep);
}
}
function _step2name(step: string|number): string {
return step.toString().replaceAll(/[^a-z0-9]+/gi,'').toUpperCase();
}
// 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)[]) {
name = `${name}${_step2name(thisStep)}_`;
path = [ ...path, thisStep ];
_walk(name, path, steps);
}
// this is the bottom of the recursion: look at the environment and
// set the value
function _lastBit(name: string, path: (string | number)[], lastStep: string | number) {
name = `MK_CONFIG_${name}${_step2name(lastStep)}`;
const val = process.env[name];
if (val != null && val != undefined) {
_assign(path, lastStep, val);
}
const file = process.env[`${name}_FILE`];
if (file) {
_assign(path, lastStep, fs.readFileSync(file, 'utf-8').trim());
}
}
const alwaysStrings = { 'chmodSocket': 1 };
function _assign(path: (string | number)[], lastStep: string | number, value: string) {
let thisConfig = config;
for (const step of path) {
if (!thisConfig[step]) {
thisConfig[step] = {};
}
thisConfig = thisConfig[step];
}
if (!alwaysStrings[lastStep]) {
if (value.match(/^[0-9]+$/)) {
value = parseInt(value);
} else if (value.match(/^(true|false)$/i)) {
value = !!value.match(/^true$/i);
}
}
thisConfig[lastStep] = value;
}
// these are all the settings that can be overridden
_apply_top([['url', 'port', 'socket', 'chmodSocket', 'disableHsts']]);
_apply_top(['db', ['host', 'port', 'db', 'user', 'pass']]);
_apply_top(['dbSlaves', config.dbSlaves?.keys(), ['host', 'port', 'db', 'user', 'pass']]);
_apply_top([
['redis', 'redisForPubsub', 'redisForJobQueue', 'redisForTimelines'],
['host','port','username','pass','db','prefix'],
]);
_apply_top(['meilisearch', ['host', 'port', 'apikey', 'ssl', 'index', 'scope']]);
_apply_top([['clusterLimit', 'deliverJobConcurrency', 'inboxJobConcurrency', 'relashionshipJobConcurrency', 'deliverJobPerSec', 'inboxJobPerSec', 'relashionshipJobPerSec', 'deliverJobMaxAttempts', 'inboxJobMaxAttempts']]);
_apply_top([['outgoingAddress', 'outgoingAddressFamily', 'proxy', 'proxySmtp', 'mediaProxy', 'videoThumbnailGenerator']]);
_apply_top([['maxFileSize', 'maxNoteLength', 'pidFile']]);
}

View file

@ -55,8 +55,6 @@ import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js'; import { infoImageUrl } from '@/instance.js';
import { defaultStore } from '@/store.js'; import { defaultStore } from '@/store.js';
console.log(defaultStore.state.noteDesign, defaultStore.state.noteDesign === 'sharkey');
const props = defineProps<{ const props = defineProps<{
pagination: Paging; pagination: Paging;
noGap?: boolean; noGap?: boolean;

View file

@ -16,9 +16,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<template #default="{ items }"> <template #default="{ items }">
<MkDateSeparatedList v-slot="{ item }" :items="items" :direction="'down'" :noGap="false" :ad="false"> <MkDateSeparatedList v-if="defaultStore.state.noteDesign === 'misskey'"
v-slot="{ item }" :items="items" :direction="'down'" :noGap="false" :ad="false">
<MkNote :key="item.id" :note="item.note" :class="$style.note"/> <MkNote :key="item.id" :note="item.note" :class="$style.note"/>
</MkDateSeparatedList> </MkDateSeparatedList>
<MkDateSeparatedList v-if="defaultStore.state.noteDesign === 'sharkey'"
v-slot="{ item }" :items="items" :direction="'down'" :noGap="false" :ad="false">
<SkNote :key="item.id" :note="item.note" :class="$style.note"/>
</MkDateSeparatedList>
</template> </template>
</MkPagination> </MkPagination>
</MkSpacer> </MkSpacer>
@ -28,10 +33,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import MkPagination from '@/components/MkPagination.vue'; import MkPagination from '@/components/MkPagination.vue';
import MkNote from '@/components/MkNote.vue'; import MkNote from '@/components/MkNote.vue';
import SkNote from '@/components/SkNote.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
import { infoImageUrl } from '@/instance.js'; import { infoImageUrl } from '@/instance.js';
import { defaultStore } from '@/store.js';
const pagination = { const pagination = {
endpoint: 'i/favorites' as const, endpoint: 'i/favorites' as const,