mirror of
https://github.com/fluxerapp/fluxer.git
synced 2026-09-02 21:04:06 +03:00
feat: add role via system to rejoining visionaries (#1198)
Co-authored-by: Hampus <hampus@fluxer.app>
This commit is contained in:
@@ -370,6 +370,7 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
|
||||
selfHosted: master.instance.self_hosted,
|
||||
autoJoinInviteCode: master.instance.auto_join_invite_code,
|
||||
visionariesGuildId: master.instance.visionaries_guild_id,
|
||||
visionariesGuildVisionaryRoleId: master.instance.visionaries_guild_visionary_role_id,
|
||||
branding: {
|
||||
productName: master.instance.branding.product_name,
|
||||
iconUrl: master.instance.branding.icon_url,
|
||||
|
||||
@@ -281,6 +281,7 @@ export interface APIConfig {
|
||||
selfHosted: boolean;
|
||||
autoJoinInviteCode?: string;
|
||||
visionariesGuildId?: string;
|
||||
visionariesGuildVisionaryRoleId?: string;
|
||||
branding: {
|
||||
productName: string;
|
||||
iconUrl?: string;
|
||||
|
||||
@@ -4,8 +4,9 @@ import {UserPremiumTypes} from '@fluxer/constants/src/UserConstants';
|
||||
import {MissingAccessError} from '@fluxer/errors/src/domains/core/MissingAccessError';
|
||||
import {StripeError} from '@fluxer/errors/src/domains/payment/StripeError';
|
||||
import type {UserID} from '../../BrandedTypes';
|
||||
import {createGuildID} from '../../BrandedTypes';
|
||||
import {createGuildID, createRoleID} from '../../BrandedTypes';
|
||||
import {Config} from '../../Config';
|
||||
import {SYSTEM_USER_ID} from '../../constants/Core';
|
||||
import type {GiftCodeDurationType} from '../../database/types/PaymentTypes';
|
||||
import type {UserRow} from '../../database/types/UserTypes';
|
||||
import type {IGuildRepositoryAggregate} from '../../guild/repositories/IGuildRepositoryAggregate';
|
||||
@@ -286,7 +287,12 @@ export class StripePremiumService {
|
||||
if (!Config.instance.visionariesGuildId) {
|
||||
throw new StripeError('Visionaries guild id not configured');
|
||||
}
|
||||
if (!Config.instance.visionariesGuildVisionaryRoleId) {
|
||||
throw new StripeError('Visionaries guild visionary role id not configured');
|
||||
}
|
||||
const visionariesGuildId = createGuildID(BigInt(Config.instance.visionariesGuildId));
|
||||
const visionaryRoleId = createRoleID(BigInt(Config.instance.visionariesGuildVisionaryRoleId));
|
||||
const requestCache = createRequestCache();
|
||||
const existingMember = await this.guildRepository.getMember(visionariesGuildId, userId);
|
||||
if (!existingMember) {
|
||||
await this.guildService.members.addUserToGuild({
|
||||
@@ -294,10 +300,25 @@ export class StripePremiumService {
|
||||
guildId: visionariesGuildId,
|
||||
sendJoinMessage: true,
|
||||
skipBanCheck: true,
|
||||
requestCache: createRequestCache(),
|
||||
requestCache,
|
||||
});
|
||||
Logger.debug({userId, guildId: visionariesGuildId}, 'Added visionary user to visionaries guild');
|
||||
}
|
||||
|
||||
try {
|
||||
await this.guildService.members.systemAddMemberRole({
|
||||
targetId: userId,
|
||||
guildId: visionariesGuildId,
|
||||
roleId: visionaryRoleId,
|
||||
initiatorId: SYSTEM_USER_ID,
|
||||
requestCache,
|
||||
});
|
||||
} catch (error) {
|
||||
Logger.error(
|
||||
{userId, guildId: visionariesGuildId, roleId: visionaryRoleId, error},
|
||||
'Failed to add visionary role to a rejoining visionary.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async dispatchUser(user: User): Promise<void> {
|
||||
|
||||
@@ -5,26 +5,31 @@ import {UserPremiumTypes} from '@fluxer/constants/src/UserConstants';
|
||||
import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest';
|
||||
import {createTestAccount} from '../../auth/tests/AuthTestUtils';
|
||||
import {Config} from '../../Config';
|
||||
import {createGuild} from '../../guild/tests/GuildTestUtils';
|
||||
import {createGuild, createRole, getMember} from '../../guild/tests/GuildTestUtils';
|
||||
import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';
|
||||
import {createBuilder} from '../../test/TestRequestBuilder';
|
||||
|
||||
describe('StripePremiumService', () => {
|
||||
let harness: ApiTestHarness;
|
||||
let originalVisionariesGuildId: string | undefined;
|
||||
let originalVisionariesGuildVisionaryRoleId: string | undefined;
|
||||
beforeAll(async () => {
|
||||
harness = await createApiTestHarness();
|
||||
originalVisionariesGuildId = Config.instance.visionariesGuildId ?? undefined;
|
||||
originalVisionariesGuildVisionaryRoleId = Config.instance.visionariesGuildVisionaryRoleId ?? undefined;
|
||||
});
|
||||
afterAll(async () => {
|
||||
await harness.shutdown();
|
||||
Config.instance.visionariesGuildId = originalVisionariesGuildId;
|
||||
Config.instance.visionariesGuildVisionaryRoleId = originalVisionariesGuildVisionaryRoleId;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await harness.resetData();
|
||||
const owner = await createTestAccount(harness);
|
||||
const visionariesGuild = await createGuild(harness, owner.token, 'Visionaries Test Guild');
|
||||
const visionaryRole = await createRole(harness, owner.token, visionariesGuild.id, {name: 'Visionary'});
|
||||
Config.instance.visionariesGuildId = visionariesGuild.id;
|
||||
Config.instance.visionariesGuildVisionaryRoleId = visionaryRole.id;
|
||||
});
|
||||
describe('POST /premium/visionary/rejoin', () => {
|
||||
test('allows visionary users to rejoin guild', async () => {
|
||||
@@ -37,6 +42,8 @@ describe('StripePremiumService', () => {
|
||||
})
|
||||
.execute();
|
||||
await createBuilder(harness, account.token).post('/premium/visionary/rejoin').expect(204).execute();
|
||||
const member = await getMember(harness, account.token, Config.instance.visionariesGuildId!, account.userId);
|
||||
expect(member.roles).toContain(Config.instance.visionariesGuildVisionaryRoleId);
|
||||
});
|
||||
test('rejects users without visionary access', async () => {
|
||||
const account = await createTestAccount(harness);
|
||||
|
||||
@@ -5,26 +5,31 @@ import {UserPremiumTypes} from '@fluxer/constants/src/UserConstants';
|
||||
import {afterAll, beforeAll, beforeEach, describe, expect, test} from 'vitest';
|
||||
import {createTestAccount} from '../../auth/tests/AuthTestUtils';
|
||||
import {Config} from '../../Config';
|
||||
import {createGuild} from '../../guild/tests/GuildTestUtils';
|
||||
import {createGuild, createRole, getMember} from '../../guild/tests/GuildTestUtils';
|
||||
import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';
|
||||
import {createBuilder} from '../../test/TestRequestBuilder';
|
||||
|
||||
describe('Stripe Webhook Edge Cases', () => {
|
||||
let harness: ApiTestHarness;
|
||||
let originalVisionariesGuildId: string | undefined;
|
||||
let originalVisionariesGuildVisionaryRoleId: string | undefined;
|
||||
beforeAll(async () => {
|
||||
harness = await createApiTestHarness();
|
||||
originalVisionariesGuildId = Config.instance.visionariesGuildId ?? undefined;
|
||||
originalVisionariesGuildVisionaryRoleId = Config.instance.visionariesGuildVisionaryRoleId ?? undefined;
|
||||
});
|
||||
afterAll(async () => {
|
||||
await harness.shutdown();
|
||||
Config.instance.visionariesGuildId = originalVisionariesGuildId;
|
||||
Config.instance.visionariesGuildVisionaryRoleId = originalVisionariesGuildVisionaryRoleId;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await harness.resetData();
|
||||
const owner = await createTestAccount(harness);
|
||||
const visionariesGuild = await createGuild(harness, owner.token, 'Visionaries Webhook Test Guild');
|
||||
const visionaryRole = await createRole(harness, owner.token, visionariesGuild.id, {name: 'Visionary'});
|
||||
Config.instance.visionariesGuildId = visionariesGuild.id;
|
||||
Config.instance.visionariesGuildVisionaryRoleId = visionaryRole.id;
|
||||
});
|
||||
describe('Premium stacking - consecutive grants extending duration', () => {
|
||||
test('stacks multiple monthly subscriptions end-to-end', async () => {
|
||||
@@ -366,6 +371,8 @@ describe('Stripe Webhook Edge Cases', () => {
|
||||
.execute();
|
||||
expect(receiverAfter.premium_type).toBe(UserPremiumTypes.LIFETIME);
|
||||
expect(receiverAfter.premium_lifetime_sequence).toBe(0);
|
||||
const member = await getMember(harness, receiver.token, Config.instance.visionariesGuildId!, receiver.userId);
|
||||
expect(member.roles).toContain(Config.instance.visionariesGuildVisionaryRoleId);
|
||||
});
|
||||
test('redeeming 1-month gift code grants subscription premium', async () => {
|
||||
const gifter = await createTestAccount(harness);
|
||||
|
||||
@@ -316,6 +316,7 @@ export interface MasterConfig {
|
||||
self_hosted: boolean;
|
||||
auto_join_invite_code?: string;
|
||||
visionaries_guild_id?: string;
|
||||
visionaries_guild_visionary_role_id?: string;
|
||||
branding: InstanceBrandingConfig;
|
||||
setup: {
|
||||
configured: boolean;
|
||||
|
||||
@@ -321,6 +321,7 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record<string, NamedEnvOverride> = {
|
||||
FLUXER_SELF_HOSTED: {path: ['instance', 'self_hosted'], parse: parseEnvValue},
|
||||
FLUXER_AUTO_JOIN_INVITE_CODE: {path: ['instance', 'auto_join_invite_code']},
|
||||
FLUXER_VISIONARIES_GUILD_ID: {path: ['instance', 'visionaries_guild_id']},
|
||||
FLUXER_VISIONARIES_GUILD_VISIONARY_ROLE_ID: {path: ['instance', 'visionaries_guild_visionary_role_id']},
|
||||
FLUXER_APP_PRODUCT_NAME: {path: ['instance', 'branding', 'product_name']},
|
||||
FLUXER_APP_ICON_URL: {path: ['instance', 'branding', 'icon_url']},
|
||||
FLUXER_APP_SYMBOL_URL: {path: ['instance', 'branding', 'symbol_url']},
|
||||
|
||||
Reference in New Issue
Block a user