mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(api): credit self-hosted gift codes to the issuing admin (#3119)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import {Config} from '@app/api/Config';
|
import {Config} from '@app/api/Config';
|
||||||
|
import {SYSTEM_USER_ID} from '@app/api/constants/Core';
|
||||||
import {requireAdminACL} from '@app/api/middleware/AdminMiddleware';
|
import {requireAdminACL} from '@app/api/middleware/AdminMiddleware';
|
||||||
import {RateLimitMiddleware} from '@app/api/middleware/RateLimitMiddleware';
|
import {RateLimitMiddleware} from '@app/api/middleware/RateLimitMiddleware';
|
||||||
import {OpenAPI} from '@app/api/middleware/ResponseTypeMiddleware';
|
import {OpenAPI} from '@app/api/middleware/ResponseTypeMiddleware';
|
||||||
@@ -42,6 +43,7 @@ export function CodesAdminController(app: HonoApp) {
|
|||||||
count,
|
count,
|
||||||
durationType: duration_type,
|
durationType: duration_type,
|
||||||
durationQuantity: duration_quantity,
|
durationQuantity: duration_quantity,
|
||||||
|
createdByUserId: Config.instance.selfHosted ? ctx.get('adminUserId') : SYSTEM_USER_ID,
|
||||||
});
|
});
|
||||||
await adminService.auditService.createAuditLog({
|
await adminService.auditService.createAuditLog({
|
||||||
adminUserId: ctx.get('adminUserId'),
|
adminUserId: ctx.get('adminUserId'),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
import {SYSTEM_USER_ID} from '@app/api/constants/Core';
|
import type {UserID} from '@app/api/BrandedTypes';
|
||||||
import type {GiftCodeDurationType, GiftCodeRow} from '@app/api/database/types/PaymentTypes';
|
import type {GiftCodeDurationType, GiftCodeRow} from '@app/api/database/types/PaymentTypes';
|
||||||
import {mapGiftCodeDurationToMonths} from '@app/api/models/GiftCode';
|
import {mapGiftCodeDurationToMonths} from '@app/api/models/GiftCode';
|
||||||
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
import type {IUserRepository} from '@app/api/user/IUserRepository';
|
||||||
@@ -15,13 +15,14 @@ interface GenerateGiftCodesOptions {
|
|||||||
count: number;
|
count: number;
|
||||||
durationType: GiftCodeDurationType;
|
durationType: GiftCodeDurationType;
|
||||||
durationQuantity: number;
|
durationQuantity: number;
|
||||||
|
createdByUserId: UserID;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AdminCodeGenerationService {
|
export class AdminCodeGenerationService {
|
||||||
constructor(private readonly userRepository: IUserRepository) {}
|
constructor(private readonly userRepository: IUserRepository) {}
|
||||||
|
|
||||||
async generateGiftCodes(options: GenerateGiftCodesOptions): Promise<Array<string>> {
|
async generateGiftCodes(options: GenerateGiftCodesOptions): Promise<Array<string>> {
|
||||||
const {count, durationType, durationQuantity} = this.validateOptions(options);
|
const {count, durationType, durationQuantity, createdByUserId} = this.validateOptions(options);
|
||||||
const durationMonths = mapGiftCodeDurationToMonths(durationType, durationQuantity);
|
const durationMonths = mapGiftCodeDurationToMonths(durationType, durationQuantity);
|
||||||
const codes: Array<string> = [];
|
const codes: Array<string> = [];
|
||||||
for (let i = 0; i < count; i += 1) {
|
for (let i = 0; i < count; i += 1) {
|
||||||
@@ -32,7 +33,7 @@ export class AdminCodeGenerationService {
|
|||||||
duration_type: durationType,
|
duration_type: durationType,
|
||||||
duration_quantity: durationQuantity,
|
duration_quantity: durationQuantity,
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
created_by_user_id: SYSTEM_USER_ID,
|
created_by_user_id: createdByUserId,
|
||||||
redeemed_at: null,
|
redeemed_at: null,
|
||||||
redeemed_by_user_id: null,
|
redeemed_by_user_id: null,
|
||||||
stripe_payment_intent_id: null,
|
stripe_payment_intent_id: null,
|
||||||
|
|||||||
@@ -284,6 +284,22 @@ describe('self-hosted premium routes', () => {
|
|||||||
expect(gifts).toEqual([]);
|
expect(gifts).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('credits admin generated gift codes to the issuing admin', async () => {
|
||||||
|
const admin = await setUserACLs(harness, await createTestAccount(harness), [
|
||||||
|
AdminACLs.AUTHENTICATE,
|
||||||
|
AdminACLs.GIFT_CODES_GENERATE,
|
||||||
|
]);
|
||||||
|
const {codes} = await createBuilder<{codes: Array<string>}>(harness, admin.token)
|
||||||
|
.post('/admin/gift-codes')
|
||||||
|
.body({count: 1, duration_type: 'months', duration_quantity: 1})
|
||||||
|
.execute();
|
||||||
|
const code = codes[0]?.split('/').pop();
|
||||||
|
const gift = await createBuilderWithoutAuth<{created_by: {id: string} | null}>(harness)
|
||||||
|
.get(`/gifts/${code}`)
|
||||||
|
.execute();
|
||||||
|
expect(gift.created_by?.id).toBe(admin.userId);
|
||||||
|
});
|
||||||
|
|
||||||
test('keeps purchase and hosted-only routes unavailable', async () => {
|
test('keeps purchase and hosted-only routes unavailable', async () => {
|
||||||
const account = await createTestAccount(harness);
|
const account = await createTestAccount(harness);
|
||||||
await expectRoutesNotFound(harness, account.token, [
|
await expectRoutesNotFound(harness, account.token, [
|
||||||
|
|||||||
Reference in New Issue
Block a user