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 { QueueModule } from './QueueModule.js';
 | 
				
			||||||
import { QueueService } from './QueueService.js';
 | 
					import { QueueService } from './QueueService.js';
 | 
				
			||||||
import { LoggerService } from './LoggerService.js';
 | 
					import { LoggerService } from './LoggerService.js';
 | 
				
			||||||
 | 
					import { SponsorsService } from './SponsorsService.js';
 | 
				
			||||||
import type { Provider } from '@nestjs/common';
 | 
					import type { Provider } from '@nestjs/common';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
					//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
				
			||||||
| 
						 | 
					@ -295,6 +296,8 @@ const $ApPersonService: Provider = { provide: 'ApPersonService', useExisting: Ap
 | 
				
			||||||
const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting: ApQuestionService };
 | 
					const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting: ApQuestionService };
 | 
				
			||||||
//#endregion
 | 
					//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const $SponsorsService: Provider = { provide: 'SponsorsService', useExisting: SponsorsService };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Module({
 | 
					@Module({
 | 
				
			||||||
	imports: [
 | 
						imports: [
 | 
				
			||||||
		QueueModule,
 | 
							QueueModule,
 | 
				
			||||||
| 
						 | 
					@ -443,6 +446,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
				
			||||||
		ApQuestionService,
 | 
							ApQuestionService,
 | 
				
			||||||
		QueueService,
 | 
							QueueService,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SponsorsService,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
							//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
				
			||||||
		$LoggerService,
 | 
							$LoggerService,
 | 
				
			||||||
		$AbuseReportService,
 | 
							$AbuseReportService,
 | 
				
			||||||
| 
						 | 
					@ -586,6 +591,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
				
			||||||
		$ApPersonService,
 | 
							$ApPersonService,
 | 
				
			||||||
		$ApQuestionService,
 | 
							$ApQuestionService,
 | 
				
			||||||
		//#endregion
 | 
							//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							$SponsorsService,
 | 
				
			||||||
	],
 | 
						],
 | 
				
			||||||
	exports: [
 | 
						exports: [
 | 
				
			||||||
		QueueModule,
 | 
							QueueModule,
 | 
				
			||||||
| 
						 | 
					@ -731,6 +738,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
				
			||||||
		ApQuestionService,
 | 
							ApQuestionService,
 | 
				
			||||||
		QueueService,
 | 
							QueueService,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SponsorsService,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
							//#region 文字列ベースでのinjection用(循環参照対応のため)
 | 
				
			||||||
		$LoggerService,
 | 
							$LoggerService,
 | 
				
			||||||
		$AbuseReportService,
 | 
							$AbuseReportService,
 | 
				
			||||||
| 
						 | 
					@ -873,6 +882,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 | 
				
			||||||
		$ApPersonService,
 | 
							$ApPersonService,
 | 
				
			||||||
		$ApQuestionService,
 | 
							$ApQuestionService,
 | 
				
			||||||
		//#endregion
 | 
							//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							$SponsorsService,
 | 
				
			||||||
	],
 | 
						],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class CoreModule { }
 | 
					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 { Inject, Injectable } from '@nestjs/common';
 | 
				
			||||||
import * as Redis from 'ioredis';
 | 
					 | 
				
			||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
 | 
					import { Endpoint } from '@/server/api/endpoint-base.js';
 | 
				
			||||||
import { DI } from '@/di-symbols.js';
 | 
					import { DI } from '@/di-symbols.js';
 | 
				
			||||||
import { MetaService } from '@/core/MetaService.js';
 | 
					import { SponsorsService } from '@/core/SponsorsService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const meta = {
 | 
					export const meta = {
 | 
				
			||||||
	tags: ['meta'],
 | 
						tags: ['meta'],
 | 
				
			||||||
| 
						 | 
					@ -29,70 +28,13 @@ export const paramDef = {
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 | 
					export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 | 
				
			||||||
	constructor(
 | 
						constructor(
 | 
				
			||||||
        @Inject(DI.redis) private redisClient: Redis.Redis,
 | 
							private sponsorsService: SponsorsService,
 | 
				
			||||||
		private metaService: MetaService,
 | 
					 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		super(meta, paramDef, async (ps, me) => {
 | 
							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) {
 | 
								if (ps.instance) {
 | 
				
			||||||
				const meta = await this.metaService.fetch();
 | 
									return { sponsor_data: await this.sponsorsService.instanceSponsors(ps.forceUpdate) };
 | 
				
			||||||
				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 [];
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}) };
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return { sponsor_data: [] };
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return { sponsor_data: await maybeCached('sponsors', ps.forceUpdate, async () => {
 | 
									return { sponsor_data: await this.sponsorsService.sharkeySponsors(ps.forceUpdate) };
 | 
				
			||||||
					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 [];
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}) };
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		});
 | 
							});
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue