Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/functions/polli-token.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
function readTokenFromEnvironment(context) {
const envSources = [];
if (context?.env) envSources.push(context.env);
if (typeof process !== 'undefined' && process?.env) envSources.push(process.env);

for (const env of envSources) {
const candidate =
env?.POLLI_TOKEN ??
env?.VITE_POLLI_TOKEN ??
env?.POLLINATIONS_TOKEN ??
env?.VITE_POLLINATIONS_TOKEN ??
null;
if (candidate != null) {
const value = String(candidate).trim();
if (value) return value;
}
}
return null;
}

export async function onRequest(context) {
const token = context?.env?.POLLI_TOKEN ?? context?.env?.VITE_POLLI_TOKEN ?? null;
const token = readTokenFromEnvironment(context);
if (!token) {
return new Response(JSON.stringify({ error: 'Pollinations token is not configured.' }), {
status: 404,
Expand Down
52 changes: 43 additions & 9 deletions src/pollinations-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,23 +145,46 @@ function readTokenFromWindow() {
}

function readTokenFromEnv() {
if (!import.meta?.env?.DEV) {
const importMetaEnv = typeof import.meta !== 'undefined' ? import.meta.env ?? undefined : undefined;
const processEnv = typeof process !== 'undefined' && process?.env ? process.env : undefined;

const isDev = determineDevelopmentEnvironment(importMetaEnv, processEnv);
if (!isDev) {
return { token: null, source: 'env' };
}
const env = import.meta.env ?? {};
const candidate =
env.VITE_POLLI_TOKEN ??
env.POLLI_TOKEN ??
env.VITE_POLLINATIONS_TOKEN ??
env.POLLINATIONS_TOKEN ??
null;
const token = extractTokenValue(candidate);

const token = extractTokenValue([
importMetaEnv?.VITE_POLLI_TOKEN,
importMetaEnv?.POLLI_TOKEN,
importMetaEnv?.VITE_POLLINATIONS_TOKEN,
importMetaEnv?.POLLINATIONS_TOKEN,
processEnv?.VITE_POLLI_TOKEN,
processEnv?.POLLI_TOKEN,
processEnv?.VITE_POLLINATIONS_TOKEN,
processEnv?.POLLINATIONS_TOKEN,
]);

if (!token) {
return { token: null, source: 'env' };
}
return { token, source: 'env' };
}

function determineDevelopmentEnvironment(importMetaEnv, processEnv) {
if (importMetaEnv && typeof importMetaEnv.DEV !== 'undefined') {
return !!importMetaEnv.DEV;
}
if (processEnv) {
if (typeof processEnv.VITE_DEV_SERVER_URL !== 'undefined') {
return true;
}
if (typeof processEnv.NODE_ENV !== 'undefined') {
return processEnv.NODE_ENV !== 'production';
}
}
return false;
}

function extractTokenValue(value) {
if (value == null) return null;
if (typeof value === 'string') {
Expand Down Expand Up @@ -213,3 +236,14 @@ function inferReferrer() {
}
return null;
}

function resetTokenCache() {
tokenPromise = null;
cachedToken = null;
cachedSource = null;
}

export const __testing = {
resetTokenCache,
determineDevelopmentEnvironment,
};
32 changes: 32 additions & 0 deletions tests/polli-token-function-env.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import assert from 'node:assert/strict';
import { onRequest } from '../.github/functions/polli-token.js';

export const name = 'Pollinations token function reads secrets from multiple environments';

export async function run() {
const originalToken = process.env.POLLI_TOKEN;
const originalViteToken = process.env.VITE_POLLI_TOKEN;

try {
delete process.env.VITE_POLLI_TOKEN;
process.env.POLLI_TOKEN = 'function-process-token';

const response = await onRequest({ env: {} });
assert.equal(response.status, 200);

const payload = await response.json();
assert.deepEqual(payload, { token: 'function-process-token' });
} finally {
if (typeof originalToken === 'undefined') {
delete process.env.POLLI_TOKEN;
} else {
process.env.POLLI_TOKEN = originalToken;
}

if (typeof originalViteToken === 'undefined') {
delete process.env.VITE_POLLI_TOKEN;
} else {
process.env.VITE_POLLI_TOKEN = originalViteToken;
}
}
}
61 changes: 61 additions & 0 deletions tests/pollinations-token-env.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import assert from 'node:assert/strict';
import { createPollinationsClient, __testing } from '../src/pollinations-client.js';

export const name = 'Pollinations client resolves tokens from development environment variables';

function createStubResponse(status = 404) {
return {
status,
ok: status >= 200 && status < 300,
headers: {
get() {
return null;
},
},
async json() {
return {};
},
async text() {
return '';
},
};
}

export async function run() {
const originalFetch = globalThis.fetch;
const originalToken = process.env.POLLI_TOKEN;
const originalNodeEnv = process.env.NODE_ENV;

try {
globalThis.fetch = async () => createStubResponse(404);
process.env.POLLI_TOKEN = 'process-env-token';
process.env.NODE_ENV = 'development';
__testing.resetTokenCache();

const { client, tokenSource } = await createPollinationsClient();
assert.equal(tokenSource, 'env');

const token = await client._auth.getToken();
assert.equal(token, 'process-env-token');
} finally {
if (originalFetch) {
globalThis.fetch = originalFetch;
} else {
delete globalThis.fetch;
}

if (typeof originalToken === 'undefined') {
delete process.env.POLLI_TOKEN;
} else {
process.env.POLLI_TOKEN = originalToken;
}

if (typeof originalNodeEnv === 'undefined') {
delete process.env.NODE_ENV;
} else {
process.env.NODE_ENV = originalNodeEnv;
}

__testing.resetTokenCache();
}
}
1 change: 1 addition & 0 deletions vite.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { defineConfig } from 'vite';
export default defineConfig({
root: '.',
base: './',
envPrefix: ['VITE_', 'POLLI_', 'POLLINATIONS_'],
build: {
outDir: 'dist',
emptyOutDir: true,
Expand Down