feat: promote users to admin

This commit is contained in:
P. Reis 2025-02-28 18:25:34 -03:00
parent 5fc874b768
commit c79867d8ca
2 changed files with 100 additions and 2 deletions

View file

@ -85,6 +85,7 @@ import {
pleromaAdminTagController, pleromaAdminTagController,
pleromaAdminUnsuggestController, pleromaAdminUnsuggestController,
pleromaAdminUntagController, pleromaAdminUntagController,
pleromaPromoteAdminController,
updateConfigController, updateConfigController,
} from '@/controllers/api/pleroma.ts'; } from '@/controllers/api/pleroma.ts';
import { preferencesController } from '@/controllers/api/preferences.ts'; import { preferencesController } from '@/controllers/api/preferences.ts';
@ -440,6 +441,11 @@ app.delete('/api/v1/pleroma/statuses/:id{[0-9a-f]{64}}/reactions/:emoji', userMi
app.get('/api/v1/pleroma/admin/config', userMiddleware({ role: 'admin' }), configController); app.get('/api/v1/pleroma/admin/config', userMiddleware({ role: 'admin' }), configController);
app.post('/api/v1/pleroma/admin/config', userMiddleware({ role: 'admin' }), updateConfigController); app.post('/api/v1/pleroma/admin/config', userMiddleware({ role: 'admin' }), updateConfigController);
app.delete('/api/v1/pleroma/admin/statuses/:id', userMiddleware({ role: 'admin' }), pleromaAdminDeleteStatusController); app.delete('/api/v1/pleroma/admin/statuses/:id', userMiddleware({ role: 'admin' }), pleromaAdminDeleteStatusController);
app.post(
'/api/v1/pleroma/admin/users/permission_group/admin',
userMiddleware({ role: 'admin' }),
pleromaPromoteAdminController,
);
app.get('/api/v1/admin/ditto/relays', userMiddleware({ role: 'admin' }), adminRelaysController); app.get('/api/v1/admin/ditto/relays', userMiddleware({ role: 'admin' }), adminRelaysController);
app.put('/api/v1/admin/ditto/relays', userMiddleware({ role: 'admin' }), adminSetRelaysController); app.put('/api/v1/admin/ditto/relays', userMiddleware({ role: 'admin' }), adminSetRelaysController);

View file

@ -1,10 +1,14 @@
import { nip19 } from 'nostr-tools';
import { z } from 'zod'; import { z } from 'zod';
import { type AppController } from '@/app.ts'; import { type AppController } from '@/app.ts';
import { configSchema, elixirTupleSchema } from '@/schemas/pleroma-api.ts'; import { createAdminEvent, parseBody, updateAdminEvent, updateUser } from '@/utils/api.ts';
import { createAdminEvent, updateAdminEvent, updateUser } from '@/utils/api.ts'; import { nostrNow } from '@/utils.ts';
import { lookupPubkey } from '@/utils/lookup.ts'; import { lookupPubkey } from '@/utils/lookup.ts';
import { getPleromaConfigs } from '@/utils/pleroma.ts'; import { getPleromaConfigs } from '@/utils/pleroma.ts';
import { renderAccount } from '@/views/mastodon/accounts.ts';
import { configSchema, elixirTupleSchema } from '@/schemas/pleroma-api.ts';
import { hydrateEvents } from '@/storages/hydrate.ts';
const frontendConfigController: AppController = async (c) => { const frontendConfigController: AppController = async (c) => {
const configDB = await getPleromaConfigs(c.var); const configDB = await getPleromaConfigs(c.var);
@ -62,6 +66,93 @@ const pleromaAdminTagSchema = z.object({
tags: z.string().array(), tags: z.string().array(),
}); });
const pleromaPromoteAdminSchema = z.object({
nicknames: z.string().transform((value, ctx) => {
try {
const { type, data } = nip19.decode(value);
if (type === 'npub') {
return data;
}
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Not a valid nip 19 npub',
});
return z.NEVER;
} catch {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Not a valid nip 19 npub',
});
return z.NEVER;
}
}).array().min(1),
});
const pleromaPromoteAdminController: AppController = async (c) => {
const { conf, relay, signal } = c.var;
const body = await parseBody(c.req.raw);
const result = pleromaPromoteAdminSchema.safeParse(body);
if (!result.success) {
return c.json({ error: 'Bad request', schema: result.error }, 422);
}
const { data } = result;
const { nicknames: authors } = data;
const events = await relay.query([{ kinds: [0], authors }], { signal });
if (events.length !== authors.length) {
return c.json({ error: 'User profile is missing in the database' }, 422);
}
events.forEach(async (event) => {
const [existing] = await relay.query([{
kinds: [30382],
authors: [await conf.signer.getPublicKey()],
'#d': [event.pubkey],
limit: 1,
}]);
const prevTags = (existing?.tags ?? []).filter(([name, value]) => {
if (name === 'd') {
return false;
}
if (name === 'n' && value === 'admin') {
return false;
}
return true;
});
const tags: string[][] = [
['d', event.pubkey],
['n', 'admin'],
];
tags.push(...prevTags);
const promotion = await conf.signer.signEvent({
kind: 30382,
tags,
content: '',
created_at: nostrNow(),
});
await relay.event(promotion);
});
await hydrateEvents({ ...c.var, events });
const accounts = events.map((event) => {
return renderAccount(event);
});
return c.json(accounts, 200);
};
const pleromaAdminTagController: AppController = async (c) => { const pleromaAdminTagController: AppController = async (c) => {
const { conf } = c.var; const { conf } = c.var;
const params = pleromaAdminTagSchema.parse(await c.req.json()); const params = pleromaAdminTagSchema.parse(await c.req.json());
@ -154,5 +245,6 @@ export {
pleromaAdminTagController, pleromaAdminTagController,
pleromaAdminUnsuggestController, pleromaAdminUnsuggestController,
pleromaAdminUntagController, pleromaAdminUntagController,
pleromaPromoteAdminController,
updateConfigController, updateConfigController,
}; };