mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 15:34:13 +00:00 
			
		
		
		
	extract SponsorsService, use RedisKVCache
Please someone add types…
This commit is contained in:
		
							parent
							
								
									f08ef28d54
								
							
						
					
					
						commit
						c2cc718f03
					
				
					 3 changed files with 101 additions and 62 deletions
				
			
		| 
						 | 
				
			
			@ -149,6 +149,7 @@ import { ApQuestionService } from './activitypub/models/ApQuestionService.js';
 | 
			
		|||
import { QueueModule } from './QueueModule.js';
 | 
			
		||||
import { QueueService } from './QueueService.js';
 | 
			
		||||
import { LoggerService } from './LoggerService.js';
 | 
			
		||||
import { SponsorsService } from './SponsorsService.js';
 | 
			
		||||
import type { Provider } from '@nestjs/common';
 | 
			
		||||
 | 
			
		||||
//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
			
		||||
| 
						 | 
				
			
			@ -295,6 +296,8 @@ const $ApPersonService: Provider = { provide: 'ApPersonService', useExisting: Ap
 | 
			
		|||
const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting: ApQuestionService };
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: SponsorsService };
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
	imports: [
 | 
			
		||||
		QueueModule,
 | 
			
		||||
| 
						 | 
				
			
			@ -443,6 +446,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		ApQuestionService,
 | 
			
		||||
		QueueService,
 | 
			
		||||
 | 
			
		||||
		SponsorsService,
 | 
			
		||||
 | 
			
		||||
		//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
			
		||||
		$LoggerService,
 | 
			
		||||
		$AbuseReportService,
 | 
			
		||||
| 
						 | 
				
			
			@ -586,6 +591,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		$ApPersonService,
 | 
			
		||||
		$ApQuestionService,
 | 
			
		||||
		//#endregion
 | 
			
		||||
 | 
			
		||||
		$SponsorsService,
 | 
			
		||||
	],
 | 
			
		||||
	exports: [
 | 
			
		||||
		QueueModule,
 | 
			
		||||
| 
						 | 
				
			
			@ -731,6 +738,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		ApQuestionService,
 | 
			
		||||
		QueueService,
 | 
			
		||||
 | 
			
		||||
		SponsorsService,
 | 
			
		||||
 | 
			
		||||
		//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
			
		||||
		$LoggerService,
 | 
			
		||||
		$AbuseReportService,
 | 
			
		||||
| 
						 | 
				
			
			@ -873,6 +882,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
			
		|||
		$ApPersonService,
 | 
			
		||||
		$ApQuestionService,
 | 
			
		||||
		//#endregion
 | 
			
		||||
 | 
			
		||||
		$SponsorsService,
 | 
			
		||||
	],
 | 
			
		||||
})
 | 
			
		||||
export class CoreModule { }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										86
									
								
								packages/backend/src/core/SponsorsService.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								packages/backend/src/core/SponsorsService.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,86 @@
 | 
			
		|||
/*
 | 
			
		||||
 * SPDX-FileCopyrightText: marie and other Sharkey contributors
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-only
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
 | 
			
		||||
import * as Redis from 'ioredis';
 | 
			
		||||
import { DI } from '@/di-symbols.js';
 | 
			
		||||
import { MetaService } from '@/core/MetaService.js';
 | 
			
		||||
import { RedisKVCache } from '@/misc/cache.js';
 | 
			
		||||
import { bindThis } from '@/decorators.js';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class SponsorsService implements OnApplicationShutdown {
 | 
			
		||||
	private cache: RedisKVCache<any>;
 | 
			
		||||
 | 
			
		||||
	constructor(
 | 
			
		||||
    @Inject(DI.redis) private redisClient: Redis.Redis,
 | 
			
		||||
		private metaService: MetaService,
 | 
			
		||||
	) {
 | 
			
		||||
		this.cache = new RedisKVCache<any>(this.redisClient, 'sponsors', {
 | 
			
		||||
			lifetime: 1000 * 60 * 60,
 | 
			
		||||
			memoryCacheLifetime: 1000 * 60,
 | 
			
		||||
			fetcher: (key) => {
 | 
			
		||||
				if (key === 'instance') return this.fetchInstanceSponsors();
 | 
			
		||||
				return this.fetchSharkeySponsors();
 | 
			
		||||
			},
 | 
			
		||||
			toRedisConverter: (value) => JSON.stringify(value),
 | 
			
		||||
			fromRedisConverter: (value) => JSON.parse(value)
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	private async fetchInstanceSponsors() {
 | 
			
		||||
		const meta = await this.metaService.fetch();
 | 
			
		||||
 | 
			
		||||
		if (!(meta.donationUrl && meta.donationUrl.includes('opencollective.com'))) {
 | 
			
		||||
			return [];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			const backers = await fetch(`${meta.donationUrl}/members/users.json`).then((response) => response.json());
 | 
			
		||||
 | 
			
		||||
			// Merge both together into one array and make sure it only has Active subscriptions
 | 
			
		||||
			const allSponsors = [...backers].filter(sponsor => sponsor.isActive === true && sponsor.role === 'BACKER' && sponsor.tier);
 | 
			
		||||
 | 
			
		||||
			// Remove possible duplicates
 | 
			
		||||
			return [...new Map(allSponsors.map(v => [v.profile, v])).values()];
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			return [];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	private async fetchSharkeySponsors() {
 | 
			
		||||
		try {
 | 
			
		||||
			const backers = await fetch('https://opencollective.com/sharkey/tiers/backer/all.json').then((response) => response.json());
 | 
			
		||||
			const sponsorsOC = await fetch('https://opencollective.com/sharkey/tiers/sponsor/all.json').then((response) => response.json());
 | 
			
		||||
 | 
			
		||||
			// Merge both together into one array and make sure it only has Active subscriptions
 | 
			
		||||
			const allSponsors = [...sponsorsOC, ...backers].filter(sponsor => sponsor.isActive === true);
 | 
			
		||||
 | 
			
		||||
			// Remove possible duplicates
 | 
			
		||||
			return [...new Map(allSponsors.map(v => [v.profile, v])).values()];
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
			return [];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async instanceSponsors(forceUpdate: boolean) {
 | 
			
		||||
		if (forceUpdate) this.cache.refresh('instance');
 | 
			
		||||
		return this.cache.fetch('instance');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public async sharkeySponsors(forceUpdate: boolean) {
 | 
			
		||||
		if (forceUpdate) this.cache.refresh('sharkey');
 | 
			
		||||
		return this.cache.fetch('sharkey');
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@bindThis
 | 
			
		||||
	public onApplicationShutdown(signal?: string | undefined): void {
 | 
			
		||||
		this.cache.dispose();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,10 +4,9 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
import { Inject, Injectable } from '@nestjs/common';
 | 
			
		||||
import * as Redis from 'ioredis';
 | 
			
		||||
import { Endpoint } from '@/server/api/endpoint-base.js';
 | 
			
		||||
import { DI } from '@/di-symbols.js';
 | 
			
		||||
import { MetaService } from '@/core/MetaService.js';
 | 
			
		||||
import { SponsorsService } from '@/core/SponsorsService.js';
 | 
			
		||||
 | 
			
		||||
export const meta = {
 | 
			
		||||
	tags: ['meta'],
 | 
			
		||||
| 
						 | 
				
			
			@ -29,70 +28,13 @@ export const paramDef = {
 | 
			
		|||
@Injectable()
 | 
			
		||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 | 
			
		||||
	constructor(
 | 
			
		||||
        @Inject(DI.redis) private redisClient: Redis.Redis,
 | 
			
		||||
		private metaService: MetaService,
 | 
			
		||||
		private sponsorsService: SponsorsService,
 | 
			
		||||
	) {
 | 
			
		||||
		super(meta, paramDef, async (ps, me) => {
 | 
			
		||||
			const maybeCached = async (key: string, forcedUpdate: boolean, fetch_cb: () => void) => {
 | 
			
		||||
				// get Key first before doing the if statement as it can be defined as either string or null
 | 
			
		||||
				const cached = await this.redisClient.get(key);
 | 
			
		||||
				
 | 
			
		||||
				if (!forcedUpdate && cached) {
 | 
			
		||||
					return JSON.parse(cached);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				try {
 | 
			
		||||
					const result = await fetch_cb();
 | 
			
		||||
					await this.redisClient.set(key, JSON.stringify(result), 'EX', 3600);
 | 
			
		||||
					return result;
 | 
			
		||||
				} catch (e) { return []; }
 | 
			
		||||
			};
 | 
			
		||||
			
 | 
			
		||||
			if (ps.instance) {
 | 
			
		||||
				const meta = await this.metaService.fetch();
 | 
			
		||||
				if (meta.donationUrl && meta.donationUrl.includes('opencollective.com')) {
 | 
			
		||||
					return { sponsor_data: await maybeCached('instanceSponsors', ps.forceUpdate, async () => {
 | 
			
		||||
						let totalSponsors;
 | 
			
		||||
						const meta = await this.metaService.fetch();
 | 
			
		||||
						
 | 
			
		||||
						try {
 | 
			
		||||
							const backers = await fetch(`${meta.donationUrl}/members/users.json`).then((response) => response.json());
 | 
			
		||||
			
 | 
			
		||||
							// Merge both together into one array and make sure it only has Active subscriptions
 | 
			
		||||
							const allSponsors = [...backers].filter(sponsor => sponsor.isActive === true && sponsor.role === 'BACKER' && sponsor.tier);
 | 
			
		||||
			
 | 
			
		||||
							// Remove possible duplicates
 | 
			
		||||
							totalSponsors = [...new Map(allSponsors.map(v => [v.profile, v])).values()];
 | 
			
		||||
									
 | 
			
		||||
							await this.redisClient.set('instanceSponsors', JSON.stringify(totalSponsors), 'EX', 3600);
 | 
			
		||||
							return totalSponsors;
 | 
			
		||||
						} catch (error) {
 | 
			
		||||
							return [];
 | 
			
		||||
						}
 | 
			
		||||
					}) };
 | 
			
		||||
				return { sponsor_data: await this.sponsorsService.instanceSponsors(ps.forceUpdate) };
 | 
			
		||||
			} else {
 | 
			
		||||
					return { sponsor_data: [] };
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				return { sponsor_data: await maybeCached('sponsors', ps.forceUpdate, async () => {
 | 
			
		||||
					let totalSponsors;
 | 
			
		||||
 | 
			
		||||
					try {
 | 
			
		||||
						const backers = await fetch('https://opencollective.com/sharkey/tiers/backer/all.json').then((response) => response.json());
 | 
			
		||||
						const sponsorsOC = await fetch('https://opencollective.com/sharkey/tiers/sponsor/all.json').then((response) => response.json());
 | 
			
		||||
	
 | 
			
		||||
						// Merge both together into one array and make sure it only has Active subscriptions
 | 
			
		||||
						const allSponsors = [...sponsorsOC, ...backers].filter(sponsor => sponsor.isActive === true);
 | 
			
		||||
	
 | 
			
		||||
						// Remove possible duplicates
 | 
			
		||||
						totalSponsors = [...new Map(allSponsors.map(v => [v.profile, v])).values()];
 | 
			
		||||
	
 | 
			
		||||
						await this.redisClient.set('sponsors', JSON.stringify(totalSponsors), 'EX', 3600);
 | 
			
		||||
						return totalSponsors;
 | 
			
		||||
					} catch (error) {
 | 
			
		||||
						return [];
 | 
			
		||||
					}
 | 
			
		||||
				}) };
 | 
			
		||||
				return { sponsor_data: await this.sponsorsService.sharkeySponsors(ps.forceUpdate) };
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue