feat: authenticated reddit requests (#365)

This commit is contained in:
Daniel Dietzler
2026-07-01 17:32:14 +02:00
committed by GitHub
parent cb7a51c4bd
commit ec2b12fa89
8 changed files with 46 additions and 6 deletions
+2
View File
@@ -9,4 +9,6 @@ interface WorkerEnv extends Omit<Env, 'ENVIRONMENT' | 'STAGE' | 'DEPLOYMENT_KEY'
GITHUB_APP_ID: string;
GITHUB_APP_PEM: string;
GITHUB_APP_INSTALLATION_ID: string;
REDDIT_OAUTH_CLIENT_ID: string;
REDDIT_OAUTH_CLIENT_SECRET: string;
}
+4 -1
View File
@@ -73,7 +73,10 @@ export default {
scheduled: async (event, env) => {
const influxProvider = new InfluxMetricsPushProvider(env.VMETRICS_DATA_API_URL, env.VMETRICS_DATA_WRITE_TOKEN);
const metricsRepository = new MetricsPushRepository('immich_data_repository', {}, [influxProvider]);
const redditWorker = new RedditIngestWorker(metricsRepository, asEnvTag(env));
const redditWorker = new RedditIngestWorker(metricsRepository, asEnvTag(env), {
clientId: env.REDDIT_OAUTH_CLIENT_ID,
clientSecret: env.REDDIT_OAUTH_CLIENT_SECRET,
});
const discordWorker = new DiscordIngestWorker(metricsRepository, asEnvTag(env));
try {
+23 -3
View File
@@ -1,4 +1,5 @@
import fetchRetry from 'fetch-retry';
import { Buffer } from 'node:buffer';
const fetch = fetchRetry(globalThis.fetch, {
retries: 3,
@@ -14,14 +15,33 @@ export interface RedditAboutResponse {
}
export class RedditRepository {
private readonly userAgent = 'Mozilla/5.0 (compatible; ImmichDataBot/1.0)';
private readonly userAgent = 'web:app.immich.data:v1.0.0 (by u/immichapp)';
async getSubredditData(subreddit: string): Promise<RedditSubredditData> {
const url = `https://www.reddit.com/r/${subreddit}/about.json`;
async getSubredditData(
subreddit: string,
oauthCredentials: { clientId: string; clientSecret: string },
): Promise<RedditSubredditData> {
const tokenRequest = await fetch('https://www.reddit.com/api/v1/access_token', {
method: 'POST',
headers: {
'User-Agent': this.userAgent,
Authorization: `Basic ${Buffer.from(`${oauthCredentials.clientId}:${oauthCredentials.clientSecret}`).toString('base64')}`,
},
body: new URLSearchParams({ grant_type: 'client_credentials' }),
});
if (!tokenRequest.ok) {
throw new Error(`Failed to authenticate against Reddit: ${tokenRequest.status} ${tokenRequest.statusText}`);
}
const { access_token } = (await tokenRequest.json()) as { access_token: string };
const url = `https://oauth.reddit.com/r/${subreddit}/about.json`;
const response = await fetch(url, {
headers: {
'User-Agent': this.userAgent,
Authorization: `Bearer ${access_token}`,
},
});
+2 -1
View File
@@ -7,11 +7,12 @@ export class RedditIngestWorker {
constructor(
private metricsRepository: IMetricsPushRepository,
private envTag: string,
private oauthCredentials: { clientId: string; clientSecret: string },
) {}
async fetchAndStoreCurrentMetrics(subreddit: string = 'immich') {
try {
const data = await this.redditRepository.getSubredditData(subreddit);
const data = await this.redditRepository.getSubredditData(subreddit, this.oauthCredentials);
const metric = new Metric('reddit_subscriber')
.intField('total', data.subscribers)
+1 -1
View File
@@ -8,7 +8,7 @@
"module": "es2022" /* Specify what module code is generated. */,
"baseUrl": "./",
"moduleResolution": "Bundler" /* Specify how TypeScript looks up a file from a given module specifier. */,
"types": ["@cloudflare/workers-types"],
"types": ["@cloudflare/workers-types", "node"],
"resolveJsonModule": true /* Enable importing .json files */,
"allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
"checkJs": false /* Enable error reporting in type-checked JavaScript files. */,
+2
View File
@@ -12,4 +12,6 @@ export TF_VAR_github_app_tofu_pem_file="op://tf/GITHUB_APP_IMMICH_TOFU/pkcs1"
export TF_VAR_github_app_read_only_installation_id="op://tf/GITHUB_APP_IMMICH_READ_ONLY/installation_id"
export TF_VAR_github_app_read_only_id="op://tf/GITHUB_APP_IMMICH_READ_ONLY/app_id"
export TF_VAR_github_app_read_only_pem_file_pkcs8="op://tf/GITHUB_APP_IMMICH_READ_ONLY/pkcs8"
export TF_VAR_reddit_oauth_client_id="op://tf/REDDIT_OAUTH_CLIENT/client_id"
export TF_VAR_reddit_oauth_client_secret="op://tf/REDDIT_OAUTH_CLIENT/client_secret"
export TF_VAR_env=$ENVIRONMENT
@@ -10,3 +10,5 @@ variable "stage" {
variable "github_app_read_only_id" {}
variable "github_app_read_only_installation_id" {}
variable "github_app_read_only_pem_file_pkcs8" {}
variable "reddit_oauth_client_id" {}
variable "reddit_oauth_client_secret" {}
@@ -165,6 +165,16 @@ resource "cloudflare_workers_script" "data_ingest_cron" {
text = var.vmetrics_data_write_token
}
secret_text_binding {
name = "REDDIT_OAUTH_CLIENT_ID"
text = var.reddit_oauth_client_id
}
secret_text_binding {
name = "REDDIT_OAUTH_CLIENT_SECRET"
text = var.reddit_oauth_client_secret
}
compatibility_date = "2025-09-17"
compatibility_flags = ["nodejs_compat"]
}