File size: 3,026 Bytes
4badc3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
 * Shared API key and admin-access helpers.
 */

import { config } from '../config.js';
import { createOpenAIError } from '../format/openai-compat.js';
import { logger } from '../utils/logger.js';

export function extractProvidedApiKey(req) {
    const authHeader = req.headers['authorization'];
    const xApiKey = req.headers['x-api-key'];

    if (authHeader && authHeader.startsWith('Bearer ')) {
        return authHeader.substring(7);
    }
    if (xApiKey) {
        return xApiKey;
    }
    return '';
}

export function extractWebuiPassword(req) {
    return req.headers['x-webui-password'] || req.query.password || '';
}

export function hasValidApiKey(req) {
    return Boolean(config.apiKey) && extractProvidedApiKey(req) === config.apiKey;
}

export function hasAdminAccess(req) {
    if (hasValidApiKey(req)) {
        return true;
    }
    if (config.webuiPassword && extractWebuiPassword(req) === config.webuiPassword) {
        return true;
    }
    return false;
}

export function requireAdminAccess(req, res, next) {
    if (hasAdminAccess(req)) {
        return next();
    }

    logger.warn(`[API] Unauthorized admin request from ${req.ip} to ${req.method} ${req.originalUrl}`);
    return res.status(401).json({
        type: 'error',
        error: {
            type: 'authentication_error',
            message: 'Authentication required'
        }
    });
}

export function createV1ApiKeyMiddleware() {
    return (req, res, next) => {
        if (!config.apiKey) {
            logger.error('[API] PROXY_API_KEY is not configured; rejecting /v1 request');
            const isOpenAIEndpoint = req.path === '/chat/completions' || req.path === '/models';
            if (isOpenAIEndpoint) {
                return res.status(503).json(createOpenAIError('Proxy API key is not configured', {
                    type: 'api_error',
                    code: 'service_unavailable'
                }));
            }
            return res.status(503).json({
                type: 'error',
                error: {
                    type: 'api_error',
                    message: 'Proxy API key is not configured'
                }
            });
        }

        const providedKey = extractProvidedApiKey(req);
        if (!providedKey || providedKey !== config.apiKey) {
            logger.warn(`[API] Unauthorized request from ${req.ip}, invalid API key`);
            const isOpenAIEndpoint = req.path === '/chat/completions' || req.path === '/models';
            if (isOpenAIEndpoint) {
                return res.status(401).json(createOpenAIError('Invalid or missing API key', {
                    type: 'authentication_error',
                    code: 'invalid_api_key'
                }));
            }
            return res.status(401).json({
                type: 'error',
                error: {
                    type: 'authentication_error',
                    message: 'Invalid or missing API key'
                }
            });
        }

        next();
    };
}