改进 token 认证
This commit is contained in:
parent
ecf813f1f6
commit
f7ebda06f7
@ -10,10 +10,7 @@ class AssistantController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*/
|
*/
|
||||||
public function index(Request $request)
|
public function index(Request $request) {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers\Web;
|
namespace App\Http\Controllers\Web;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Logic\OpenIDLogic;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\Exception\ClientException;
|
use GuzzleHttp\Exception\ClientException;
|
||||||
@ -18,11 +19,7 @@ class AuthController extends Controller
|
|||||||
{
|
{
|
||||||
public string $scopes = 'profile email realname openid';
|
public string $scopes = 'profile email realname openid';
|
||||||
|
|
||||||
protected string $auth_url;
|
protected OpenIDLogic $openIDLogic;
|
||||||
|
|
||||||
protected string $token_url;
|
|
||||||
|
|
||||||
protected string $user_url;
|
|
||||||
|
|
||||||
protected string $callback_url;
|
protected string $callback_url;
|
||||||
|
|
||||||
@ -33,24 +30,7 @@ class AuthController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$cache_key = 'oauth_discovery';
|
$this->openIDLogic = app(OpenIDLogic::class);
|
||||||
|
|
||||||
if (cache()->has($cache_key)) {
|
|
||||||
$oauth_discovery = cache()->get($cache_key);
|
|
||||||
} else {
|
|
||||||
// lock 防止重复请求
|
|
||||||
$oauth_discovery = cache()->remember($cache_key, 3600, function () {
|
|
||||||
$client = new Client();
|
|
||||||
$response = $client->get(config('oauth.discovery'));
|
|
||||||
|
|
||||||
return json_decode($response->getBody(), true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->auth_url = $oauth_discovery['authorization_endpoint'];
|
|
||||||
$this->token_url = $oauth_discovery['token_endpoint'];
|
|
||||||
$this->user_url = $oauth_discovery['userinfo_endpoint'];
|
|
||||||
$this->callback_url = route('oauth.callback');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function redirect(Request $request)
|
public function redirect(Request $request)
|
||||||
@ -65,7 +45,7 @@ public function redirect(Request $request)
|
|||||||
'state' => $state,
|
'state' => $state,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->to($this->auth_url.'?'.$query);
|
return redirect()->to($this->openIDLogic->auth_url.'?'.$query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +67,7 @@ public function callback(Request $request)
|
|||||||
$http = new Client;
|
$http = new Client;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$authorize = $http->post($this->token_url, [
|
$authorize = $http->post($this->openIDLogic->token_url, [
|
||||||
'form_params' => [
|
'form_params' => [
|
||||||
'grant_type' => 'authorization_code',
|
'grant_type' => 'authorization_code',
|
||||||
'client_id' => config('oauth.client_id'),
|
'client_id' => config('oauth.client_id'),
|
||||||
@ -102,7 +82,7 @@ public function callback(Request $request)
|
|||||||
|
|
||||||
$authorize = json_decode($authorize->getBody());
|
$authorize = json_decode($authorize->getBody());
|
||||||
|
|
||||||
$oauth_user = $http->get($this->user_url, [
|
$oauth_user = $http->get($this->openIDLogic->user_url, [
|
||||||
'headers' => [
|
'headers' => [
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
'Authorization' => 'Bearer '.$authorize->access_token,
|
'Authorization' => 'Bearer '.$authorize->access_token,
|
||||||
|
@ -8,9 +8,7 @@
|
|||||||
use cebe\openapi\exceptions\TypeErrorException;
|
use cebe\openapi\exceptions\TypeErrorException;
|
||||||
use cebe\openapi\exceptions\UnresolvableReferenceException;
|
use cebe\openapi\exceptions\UnresolvableReferenceException;
|
||||||
use cebe\openapi\json\InvalidJsonPointerSyntaxException;
|
use cebe\openapi\json\InvalidJsonPointerSyntaxException;
|
||||||
use cebe\openapi\Reader;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class ToolController extends Controller
|
class ToolController extends Controller
|
||||||
{
|
{
|
||||||
@ -28,12 +26,12 @@ public function index()
|
|||||||
* @throws UnresolvableReferenceException
|
* @throws UnresolvableReferenceException
|
||||||
* @throws InvalidJsonPointerSyntaxException
|
* @throws InvalidJsonPointerSyntaxException
|
||||||
*/
|
*/
|
||||||
public function getOpenAPI() {
|
public function getOpenAPI()
|
||||||
$url = "http://127.0.0.1:8081/openapi.yml";
|
{
|
||||||
|
$url = 'http://127.0.0.1:8081/openapi.yml';
|
||||||
|
|
||||||
$file = file_get_contents($url);
|
$file = file_get_contents($url);
|
||||||
|
|
||||||
|
|
||||||
// $openAPI = new OpenAPI();
|
// $openAPI = new OpenAPI();
|
||||||
//
|
//
|
||||||
// $document = $openAPI->parse($file);
|
// $document = $openAPI->parse($file);
|
||||||
|
60
app/Http/Middleware/JWTMiddleware.php
Normal file
60
app/Http/Middleware/JWTMiddleware.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Web\AuthController;
|
||||||
|
use App\Models\User;
|
||||||
|
use Closure;
|
||||||
|
use Exception;
|
||||||
|
use Firebase\JWT\JWK;
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use stdClass;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class JWTMiddleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Handle an incoming request.
|
||||||
|
*
|
||||||
|
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next, string $token_type = 'id_token'): Response
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$jwks = (new AuthController)->getJwks();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return response()->json(['error' => 'Failed to fetch JWKS data'], 500);
|
||||||
|
}
|
||||||
|
$keys = JWK::parseKeySet($jwks);
|
||||||
|
|
||||||
|
$jwt = $request->bearerToken();
|
||||||
|
|
||||||
|
if (empty($jwt)) {
|
||||||
|
return response()->json(['error' => 'No token provided'], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = new stdClass();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 解码并验证 JWT
|
||||||
|
$decoded = JWT::decode($jwt, $keys, $headers);
|
||||||
|
|
||||||
|
if ($headers->typ != $token_type) {
|
||||||
|
return response()->json(['error' => 'Invalid token type, must be '.$token_type.', got '.$headers->typ], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = User::where('external_id', $decoded->sub)->firstOrCreate([
|
||||||
|
'external_id' => $decoded->sub,
|
||||||
|
'name' => $decoded->name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Auth::guard('api')->loginUsingId($user->id, true);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return response()->json(['error' => 'Invalid token, '.$e->getMessage()], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,4 @@
|
|||||||
|
|
||||||
namespace App\LLM;
|
namespace App\LLM;
|
||||||
|
|
||||||
class Base
|
class Base {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
56
app/Logic/OpenIDLogic.php
Normal file
56
app/Logic/OpenIDLogic.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Logic;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
|
||||||
|
class OpenIDLogic
|
||||||
|
{
|
||||||
|
public string $auth_url;
|
||||||
|
|
||||||
|
public string $token_url;
|
||||||
|
|
||||||
|
public string $user_url;
|
||||||
|
|
||||||
|
public string $jwks_url;
|
||||||
|
|
||||||
|
public array $jwks = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new class instance.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$http = new Client();
|
||||||
|
$cache_key = 'oauth_discovery';
|
||||||
|
|
||||||
|
if (cache()->has($cache_key)) {
|
||||||
|
$oauth_discovery = cache()->get($cache_key);
|
||||||
|
} else {
|
||||||
|
// lock 防止重复请求
|
||||||
|
$oauth_discovery = cache()->remember($cache_key, 3600, function () use ($http) {
|
||||||
|
$response = $http->get(config('oauth.discovery'));
|
||||||
|
|
||||||
|
return json_decode($response->getBody(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->auth_url = $oauth_discovery['authorization_endpoint'];
|
||||||
|
$this->token_url = $oauth_discovery['token_endpoint'];
|
||||||
|
$this->user_url = $oauth_discovery['userinfo_endpoint'];
|
||||||
|
$this->jwks_url = $oauth_discovery['jwks_uri'];
|
||||||
|
|
||||||
|
$cache_key = 'oauth_discovery_jwks';
|
||||||
|
|
||||||
|
if (cache()->has($cache_key)) {
|
||||||
|
$this->jwks = cache()->get($cache_key);
|
||||||
|
} else {
|
||||||
|
$this->jwks = cache()->remember($cache_key, 3600, function () use ($http) {
|
||||||
|
$response = $http->get($this->jwks_url);
|
||||||
|
|
||||||
|
return json_decode($response->getBody(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,36 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
class Assistant extends Model
|
class Assistant extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'user_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tools(): BelongsToMany
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Tool::class, 'assistant_tools');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function chats(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Chat::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function chatHistories(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(ChatHistory::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
class AssistantTool extends Model
|
class AssistantTool extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
protected $fillable = [
|
||||||
|
'assistant_id',
|
||||||
|
'tool_id',
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
31
app/Models/Chat.php
Normal file
31
app/Models/Chat.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
class Chat extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'assistant_id',
|
||||||
|
'user_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function assistant(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Assistant::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function histories(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(ChatHistory::class);
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,22 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
class ChatHistory extends Model
|
class ChatHistory extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
protected $fillable = [
|
||||||
|
'chat_id',
|
||||||
|
'content',
|
||||||
|
'role',
|
||||||
|
'input_tokens',
|
||||||
|
'output_tokens',
|
||||||
|
'total_tokens',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function chat(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Chat::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,41 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class Tool extends Model
|
class Tool extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'url',
|
||||||
|
'api_key',
|
||||||
|
'callback_url',
|
||||||
|
'user_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function functions(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(ToolFunction::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchFunctions(): void
|
||||||
|
{
|
||||||
|
$url = $this->url;
|
||||||
|
|
||||||
|
$json = Http::get($url);
|
||||||
|
|
||||||
|
$json = $json->json();
|
||||||
|
|
||||||
|
$this->callback_url = $json['callback_url'];
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,41 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
class ToolFunction extends Model
|
class ToolFunction extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
protected $fillable = [
|
||||||
|
'tool_id',
|
||||||
|
'name',
|
||||||
|
'description',
|
||||||
|
'parameters',
|
||||||
|
'required',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function tool(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Tool::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParametersAttribute($value)
|
||||||
|
{
|
||||||
|
return json_decode($value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredAttribute($value)
|
||||||
|
{
|
||||||
|
return json_decode($value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setParametersAttribute($value): void
|
||||||
|
{
|
||||||
|
$this->attributes['parameters'] = json_encode($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRequiredAttribute($value): void
|
||||||
|
{
|
||||||
|
$this->attributes['required'] = json_encode($value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,30 +18,31 @@ class User extends Authenticatable
|
|||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
'email',
|
'external_id',
|
||||||
'password',
|
// 'email',
|
||||||
|
// 'password',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* The attributes that should be hidden for serialization.
|
// * The attributes that should be hidden for serialization.
|
||||||
*
|
// *
|
||||||
* @var array<int, string>
|
// * @var array<int, string>
|
||||||
*/
|
// */
|
||||||
protected $hidden = [
|
// protected $hidden = [
|
||||||
'password',
|
// 'password',
|
||||||
'remember_token',
|
// 'remember_token',
|
||||||
];
|
// ];
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Get the attributes that should be cast.
|
// * Get the attributes that should be cast.
|
||||||
*
|
// *
|
||||||
* @return array<string, string>
|
// * @return array<string, string>
|
||||||
*/
|
// */
|
||||||
protected function casts(): array
|
// protected function casts(): array
|
||||||
{
|
// {
|
||||||
return [
|
// return [
|
||||||
'email_verified_at' => 'datetime',
|
// 'email_verified_at' => 'datetime',
|
||||||
'password' => 'hashed',
|
// 'password' => 'hashed',
|
||||||
];
|
// ];
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Logic\OpenIDLogic;
|
||||||
|
use App\Models\User;
|
||||||
|
use Exception;
|
||||||
|
use Firebase\JWT\JWK;
|
||||||
|
use Firebase\JWT\JWT;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@ -19,6 +27,43 @@ public function register(): void
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
//
|
$this->setJWTGuard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setJWTGuard(): void
|
||||||
|
{
|
||||||
|
Auth::viaRequest('jwt', function (Request $request) {
|
||||||
|
$logic = app(OpenIDLogic::class);
|
||||||
|
|
||||||
|
$keys = JWK::parseKeySet($logic->jwks);
|
||||||
|
|
||||||
|
$jwt = $request->bearerToken();
|
||||||
|
|
||||||
|
if (empty($jwt)) {
|
||||||
|
return response()->json(['error' => 'No token provided'], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = new stdClass();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$decoded = JWT::decode($jwt, $keys, $headers);
|
||||||
|
$request->attributes->add(['token_type' => $headers->typ]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return response()->json(['error' => 'Invalid token, '.$e->getMessage()], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! in_array($decoded->aud, config('oauth.trusted_aud'))) {
|
||||||
|
return response()->json(['error' => 'The application rejected the token, token aud is '.$decoded->aud.', app aud is '.config('oauth.client_id')], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config('oauth.force_aud') && $decoded->aud != config('oauth.client_id')) {
|
||||||
|
return response()->json(['error' => 'The token not match the application, '.' token aud is '.$decoded->aud.', app aud is '.config('oauth.client_id')], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
return User::where('external_id', $decoded->sub)->firstOrCreate([
|
||||||
|
'external_id' => $decoded->sub,
|
||||||
|
'name' => $decoded->name,
|
||||||
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
49
app/Repositories/Tool/Tool.php
Normal file
49
app/Repositories/Tool/Tool.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repositories\Tool;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class Tool
|
||||||
|
{
|
||||||
|
public string $name;
|
||||||
|
|
||||||
|
public string $url;
|
||||||
|
|
||||||
|
public string $description;
|
||||||
|
|
||||||
|
public string $api_key;
|
||||||
|
|
||||||
|
public string $user_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function parse(array $data): void
|
||||||
|
{
|
||||||
|
// 验证数据
|
||||||
|
if (! $this->validate($data)) {
|
||||||
|
throw new Exception('Invalid data');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->name = $data['name'];
|
||||||
|
$this->url = $data['url'];
|
||||||
|
$this->description = $data['description'];
|
||||||
|
$this->api_key = $data['api_key'];
|
||||||
|
$this->user_id = $data['user_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(array $data): bool
|
||||||
|
{
|
||||||
|
// all fields are required
|
||||||
|
if (empty($data['name']) ||
|
||||||
|
empty($data['url']) ||
|
||||||
|
empty($data['description']) ||
|
||||||
|
empty($data['api_key']) ||
|
||||||
|
empty($data['user_id'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
14
app/Repositories/Tool/ToolFunction.php
Normal file
14
app/Repositories/Tool/ToolFunction.php
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repositories\Tool;
|
||||||
|
|
||||||
|
class ToolFunction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new class instance.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,9 @@
|
|||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"apiboard/php-openapi": "^2.1",
|
"apiboard/php-openapi": "^2.1",
|
||||||
"cebe/php-openapi": "^1.7",
|
"cebe/php-openapi": "^1.7",
|
||||||
|
"firebase/php-jwt": "^6.10",
|
||||||
"laravel/framework": "^11.9",
|
"laravel/framework": "^11.9",
|
||||||
|
"laravel/sanctum": "^4.0",
|
||||||
"laravel/tinker": "^2.9"
|
"laravel/tinker": "^2.9"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
129
composer.lock
generated
129
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "5d6d988753b4895d093e83b35b767cfb",
|
"content-hash": "1934fe35f6ca19ea328e64d7a0773787",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "apiboard/php-openapi",
|
"name": "apiboard/php-openapi",
|
||||||
@ -618,6 +618,69 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-10-06T06:47:41+00:00"
|
"time": "2023-10-06T06:47:41+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "firebase/php-jwt",
|
||||||
|
"version": "v6.10.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/firebase/php-jwt.git",
|
||||||
|
"reference": "500501c2ce893c824c801da135d02661199f60c5"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5",
|
||||||
|
"reference": "500501c2ce893c824c801da135d02661199f60c5",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
|
"phpspec/prophecy-phpunit": "^2.0",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"psr/cache": "^2.0||^3.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
|
"psr/http-factory": "^1.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-sodium": "Support EdDSA (Ed25519) signatures",
|
||||||
|
"paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Firebase\\JWT\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Neuman Vong",
|
||||||
|
"email": "neuman+pear@twilio.com",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Anant Narayanan",
|
||||||
|
"email": "anant@php.net",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
|
||||||
|
"homepage": "https://github.com/firebase/php-jwt",
|
||||||
|
"keywords": [
|
||||||
|
"jwt",
|
||||||
|
"php"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/firebase/php-jwt/issues",
|
||||||
|
"source": "https://github.com/firebase/php-jwt/tree/v6.10.1"
|
||||||
|
},
|
||||||
|
"time": "2024-05-18T18:05:11+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "fruitcake/php-cors",
|
"name": "fruitcake/php-cors",
|
||||||
"version": "v1.3.0",
|
"version": "v1.3.0",
|
||||||
@ -1491,6 +1554,70 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-06-17T13:58:22+00:00"
|
"time": "2024-06-17T13:58:22+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "laravel/sanctum",
|
||||||
|
"version": "v4.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/laravel/sanctum.git",
|
||||||
|
"reference": "9cfc0ce80cabad5334efff73ec856339e8ec1ac1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/laravel/sanctum/zipball/9cfc0ce80cabad5334efff73ec856339e8ec1ac1",
|
||||||
|
"reference": "9cfc0ce80cabad5334efff73ec856339e8ec1ac1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"illuminate/console": "^11.0",
|
||||||
|
"illuminate/contracts": "^11.0",
|
||||||
|
"illuminate/database": "^11.0",
|
||||||
|
"illuminate/support": "^11.0",
|
||||||
|
"php": "^8.2",
|
||||||
|
"symfony/console": "^7.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.6",
|
||||||
|
"orchestra/testbench": "^9.0",
|
||||||
|
"phpstan/phpstan": "^1.10",
|
||||||
|
"phpunit/phpunit": "^10.5"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Laravel\\Sanctum\\SanctumServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Laravel\\Sanctum\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Taylor Otwell",
|
||||||
|
"email": "taylor@laravel.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
|
||||||
|
"keywords": [
|
||||||
|
"auth",
|
||||||
|
"laravel",
|
||||||
|
"sanctum"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/laravel/sanctum/issues",
|
||||||
|
"source": "https://github.com/laravel/sanctum"
|
||||||
|
},
|
||||||
|
"time": "2024-04-10T19:39:58+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/serializable-closure",
|
"name": "laravel/serializable-closure",
|
||||||
"version": "v1.3.3",
|
"version": "v1.3.3",
|
||||||
|
83
config/sanctum.php
Normal file
83
config/sanctum.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Laravel\Sanctum\Sanctum;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Stateful Domains
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Requests from the following domains / hosts will receive stateful API
|
||||||
|
| authentication cookies. Typically, these should include your local
|
||||||
|
| and production domains which access your API via a frontend SPA.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
||||||
|
'%s%s',
|
||||||
|
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
||||||
|
Sanctum::currentApplicationUrlWithPort()
|
||||||
|
))),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Sanctum Guards
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This array contains the authentication guards that will be checked when
|
||||||
|
| Sanctum is trying to authenticate a request. If none of these guards
|
||||||
|
| are able to authenticate the request, Sanctum will use the bearer
|
||||||
|
| token that's present on an incoming request for authentication.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'guard' => ['web'],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Expiration Minutes
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| This value controls the number of minutes until an issued token will be
|
||||||
|
| considered expired. This will override any values set in the token's
|
||||||
|
| "expires_at" attribute, but first-party sessions are not affected.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'expiration' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Token Prefix
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Sanctum can prefix new tokens in order to take advantage of numerous
|
||||||
|
| security scanning initiatives maintained by open source platforms
|
||||||
|
| that notify developers if they commit tokens into repositories.
|
||||||
|
|
|
||||||
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Sanctum Middleware
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| When authenticating your first-party SPA with Sanctum you may need to
|
||||||
|
| customize some of the middleware Sanctum uses while processing the
|
||||||
|
| request. You may change the middleware listed below as required.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'middleware' => [
|
||||||
|
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
|
||||||
|
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||||||
|
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
@ -14,27 +14,27 @@ public function up(): void
|
|||||||
Schema::create('users', function (Blueprint $table) {
|
Schema::create('users', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('email')->unique();
|
$table->unsignedBigInteger('external_id')->index();
|
||||||
$table->timestamp('email_verified_at')->nullable();
|
// $table->string('email')->unique();
|
||||||
$table->string('password');
|
// $table->timestamp('email_verified_at')->nullable();
|
||||||
$table->rememberToken();
|
// $table->rememberToken();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
// Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||||
$table->string('email')->primary();
|
// $table->string('email')->primary();
|
||||||
$table->string('token');
|
// $table->string('token');
|
||||||
$table->timestamp('created_at')->nullable();
|
// $table->timestamp('created_at')->nullable();
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
Schema::create('sessions', function (Blueprint $table) {
|
// Schema::create('sessions', function (Blueprint $table) {
|
||||||
$table->string('id')->primary();
|
// $table->string('id')->primary();
|
||||||
$table->foreignId('user_id')->nullable()->index();
|
// $table->foreignId('user_id')->nullable()->index();
|
||||||
$table->string('ip_address', 45)->nullable();
|
// $table->string('ip_address', 45)->nullable();
|
||||||
$table->text('user_agent')->nullable();
|
// $table->text('user_agent')->nullable();
|
||||||
$table->longText('payload');
|
// $table->longText('payload');
|
||||||
$table->integer('last_activity')->index();
|
// $table->integer('last_activity')->index();
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,7 +21,6 @@ public function up(): void
|
|||||||
$table->unsignedBigInteger('user_id')->index();
|
$table->unsignedBigInteger('user_id')->index();
|
||||||
$table->foreign('user_id')->references('id')->on('users');
|
$table->foreign('user_id')->references('id')->on('users');
|
||||||
|
|
||||||
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ public function up(): void
|
|||||||
// index
|
// index
|
||||||
$table->index(['assistant_id', 'user_id']);
|
$table->index(['assistant_id', 'user_id']);
|
||||||
|
|
||||||
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ public function up(): void
|
|||||||
// index
|
// index
|
||||||
$table->index(['chat_id', 'role']);
|
$table->index(['chat_id', 'role']);
|
||||||
|
|
||||||
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('tools', function (Blueprint $table) {
|
||||||
|
$table->string('callback_url')->after('api_key');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('tools', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('callback_url');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
8
routes/api.php
Normal file
8
routes/api.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::get('/user', function (Request $request) {
|
||||||
|
return $request->user();
|
||||||
|
})->middleware('auth:api');
|
Loading…
Reference in New Issue
Block a user