feat: Add Bearer Token authentication to AI Proxy and disable CORS
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f2f6acd070
commit
3502694d89
|
|
@ -33,6 +33,23 @@ class Secrets {
|
|||
/// AI Proxy サーバーの解析エンドポイントURL
|
||||
static const String aiProxyAnalyzeUrl = '$aiProxyBaseUrl/analyze';
|
||||
|
||||
/// AI Proxy 認証トークン
|
||||
/// Proxy経由モード(useProxy=true)時に使用するBearer Token
|
||||
/// ビルド時の上書き: --dart-define=PROXY_AUTH_TOKEN=...
|
||||
static const String _proxyAuthTokenEnv = String.fromEnvironment(
|
||||
'PROXY_AUTH_TOKEN',
|
||||
defaultValue: '',
|
||||
);
|
||||
|
||||
/// 実際に使用するProxy認証トークン
|
||||
/// 優先順位: 環境変数 > ローカル設定ファイル
|
||||
static String get proxyAuthToken {
|
||||
if (_proxyAuthTokenEnv.isNotEmpty) {
|
||||
return _proxyAuthTokenEnv;
|
||||
}
|
||||
return local.SecretsLocal.proxyAuthToken;
|
||||
}
|
||||
|
||||
/// Gemini API Key
|
||||
/// ⚠️ セキュリティのため、defaultValueは空です
|
||||
/// ローカル開発時: lib/secrets.local.dart を作成してキーを設定
|
||||
|
|
|
|||
|
|
@ -15,4 +15,7 @@ class SecretsLocal {
|
|||
|
||||
/// ローカル開発時のAI Proxy URL(オプション)
|
||||
static const String aiProxyBaseUrl = 'http://100.76.7.3:8080';
|
||||
|
||||
/// AI Proxy認証トークン(Proxy経由モード時に使用)
|
||||
static const String proxyAuthToken = 'YOUR_PROXY_AUTH_TOKEN_HERE';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,10 +174,15 @@ $extractedText
|
|||
|
||||
debugPrint('Calling Proxy: $_proxyUrl');
|
||||
|
||||
// 5. 送信
|
||||
// 5. 送信(Bearer Token認証付き)
|
||||
final headers = {
|
||||
"Content-Type": "application/json",
|
||||
if (Secrets.proxyAuthToken.isNotEmpty)
|
||||
"Authorization": "Bearer ${Secrets.proxyAuthToken}",
|
||||
};
|
||||
final response = await http.post(
|
||||
Uri.parse(_proxyUrl),
|
||||
headers: {"Content-Type": "application/json"},
|
||||
headers: headers,
|
||||
body: requestBody,
|
||||
).timeout(const Duration(seconds: 60)); // 拡張: 60秒 (画像サイズ最適化済み)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,46 @@
|
|||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const cors = require('cors');
|
||||
const { GoogleGenerativeAI } = require('@google/generative-ai');
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 8080;
|
||||
const API_KEY = process.env.GEMINI_API_KEY;
|
||||
const AUTH_TOKEN = process.env.PROXY_AUTH_TOKEN;
|
||||
|
||||
// Rate Limiting (Simple In-Memory)
|
||||
const DAILY_LIMIT = parseInt(process.env.DAILY_LIMIT || '50', 10);
|
||||
const usageStore = {}; // { deviceId: { date: 'YYYY-MM-DD', count: 0 } }
|
||||
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json({ limit: '10mb' })); // Allow large image payloads
|
||||
// Authentication Middleware (skip for /health)
|
||||
function authMiddleware(req, res, next) {
|
||||
if (!AUTH_TOKEN) {
|
||||
// If no token configured, skip auth (backward compatibility)
|
||||
console.warn('[Auth] WARNING: PROXY_AUTH_TOKEN is not set. Authentication disabled.');
|
||||
return next();
|
||||
}
|
||||
|
||||
const authHeader = req.headers['authorization'];
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
console.log(`[Auth] Rejected: Missing or invalid Authorization header`);
|
||||
return res.status(401).json({ success: false, error: 'Authentication required' });
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
|
||||
if (token !== AUTH_TOKEN) {
|
||||
console.log(`[Auth] Rejected: Invalid token`);
|
||||
return res.status(403).json({ success: false, error: 'Invalid authentication token' });
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
// Global middleware: Auth first (skip /health), then body parser
|
||||
app.use((req, res, next) => {
|
||||
if (req.path === '/health') return next();
|
||||
authMiddleware(req, res, next);
|
||||
});
|
||||
app.use(bodyParser.json({ limit: '10mb' }));
|
||||
|
||||
// Gemini Client
|
||||
const genAI = new GoogleGenerativeAI(API_KEY);
|
||||
|
|
@ -48,7 +75,7 @@ function checkRateLimit(deviceId) {
|
|||
};
|
||||
}
|
||||
|
||||
// API Endpoint
|
||||
// API Endpoint (authentication enforced by global middleware)
|
||||
app.post('/analyze', async (req, res) => {
|
||||
const { device_id, images, prompt } = req.body;
|
||||
|
||||
|
|
@ -126,4 +153,6 @@ app.get('/health', (req, res) => {
|
|||
app.listen(PORT, () => {
|
||||
console.log(`Proxy Server running on port ${PORT}`);
|
||||
if (!API_KEY) console.error('WARNING: GEMINI_API_KEY is not set!');
|
||||
if (!AUTH_TOKEN) console.error('WARNING: PROXY_AUTH_TOKEN is not set! Authentication is disabled.');
|
||||
else console.log('Authentication: Bearer Token enabled');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue