release
parent
0274737a4c
commit
9dbcdbd407
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Events\Auth;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class Registered
|
||||||
|
{
|
||||||
|
use Dispatchable;
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public User $user,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\V1\Auth;
|
||||||
|
|
||||||
|
use App\Events\Auth\Registered;
|
||||||
|
use App\Exceptions\BizException;
|
||||||
|
use App\Http\Controllers\Api\V1\Controller;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\UserInfo;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class RegisterController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'phone' => ['bail', 'required', Rule::unique(User::class)],
|
||||||
|
'password' => ['bail', 'required', 'string', 'min:6', 'max:32'],
|
||||||
|
'verify_code' => ['bail', 'required', 'string'],
|
||||||
|
'code' => ['bail', 'nullable', 'string'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// @todo 验证短信验证码
|
||||||
|
|
||||||
|
$inviter = null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
$request->filled('code')
|
||||||
|
&& is_null($inviter = UserInfo::where('code', $request->input('code'))->first())
|
||||||
|
) {
|
||||||
|
throw new BizException(__('Invalid invitation code'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = now();
|
||||||
|
$ip = $request->realIp();
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
$user = new User($request->only(['phone', 'password']));
|
||||||
|
$user->phone_verified_at = $time;
|
||||||
|
$user->register_ip = $ip;
|
||||||
|
$user->last_login_at = $time;
|
||||||
|
$user->last_login_ip = $ip;
|
||||||
|
$user->setCreatedAt($time);
|
||||||
|
$user->setUpdatedAt($time);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$user->userInfo()->create([
|
||||||
|
'inviter_id' => $inviter?->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
report($e);
|
||||||
|
|
||||||
|
throw new BizException(__('Registration failed, please try again'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Registered::dispatch($user);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'token' => $user->createToken('app')->plainTextToken,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Api\V1;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller as BaseController;
|
||||||
|
|
||||||
|
class Controller extends BaseController
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -7,14 +7,17 @@ use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
||||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
||||||
class User extends Model implements AuthorizableContract, AuthenticatableContract
|
class User extends Model implements AuthorizableContract, AuthenticatableContract
|
||||||
{
|
{
|
||||||
use Authenticatable;
|
use Authenticatable;
|
||||||
use Authorizable;
|
use Authorizable;
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
use HasApiTokens;
|
||||||
|
|
||||||
public const STATUS_FROZEN = -1; // 冻结
|
public const STATUS_FROZEN = -1; // 冻结
|
||||||
public const STATUS_INACTIVATED = 0; // 未激活
|
public const STATUS_INACTIVATED = 0; // 未激活
|
||||||
|
|
@ -54,6 +57,16 @@ class User extends Model implements AuthorizableContract, AuthenticatableContrac
|
||||||
'status' => 'int',
|
'status' => 'int',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属于此用户的个人信息
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||||
|
*/
|
||||||
|
public function userInfo(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(UserInfo::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置此用户的密码
|
* 设置此用户的密码
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,11 @@ class UserInfo extends Model
|
||||||
// 如果没有邀请码,则自动分配邀请码
|
// 如果没有邀请码,则自动分配邀请码
|
||||||
if ($userInfo->code === null) {
|
if ($userInfo->code === null) {
|
||||||
do {
|
do {
|
||||||
$userInfo->code = Str::randomAlpha(6);
|
$userInfo->code = strtolower(Str::randomAlpha(6));
|
||||||
} while (static::where('code', $userInfo->code)->exists());
|
} while (static::where('code', $userInfo->code)->exists());
|
||||||
|
} elseif ($userInfo->isDirty('code')) {
|
||||||
|
$userInfo->code = strtolower($userInfo->code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 邀请码统一小写
|
|
||||||
$userInfo->code = strtolower($userInfo->code);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"guzzlehttp/guzzle": "^7.0.1",
|
"guzzlehttp/guzzle": "^7.0.1",
|
||||||
"kalnoy/nestedset": "^6.0",
|
"kalnoy/nestedset": "^6.0",
|
||||||
"laravel/framework": "^8.65",
|
"laravel/framework": "^8.65",
|
||||||
|
"laravel/sanctum": "^2.12",
|
||||||
"laravel/tinker": "^2.5"
|
"laravel/tinker": "^2.5"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|
|
||||||
|
|
@ -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": "b3f1c661b5bcaf6d0066e81774a3a2ad",
|
"content-hash": "50de900b3113c2d3d3f9c191fd6e6693",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "asm89/stack-cors",
|
"name": "asm89/stack-cors",
|
||||||
|
|
@ -1721,6 +1721,76 @@
|
||||||
},
|
},
|
||||||
"time": "2021-11-09T22:48:33+00:00"
|
"time": "2021-11-09T22:48:33+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "laravel/sanctum",
|
||||||
|
"version": "v2.12.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/laravel/sanctum.git",
|
||||||
|
"reference": "76b2d552c00477d520338889160f80a0cfb5fc55"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/laravel/sanctum/zipball/76b2d552c00477d520338889160f80a0cfb5fc55",
|
||||||
|
"reference": "76b2d552c00477d520338889160f80a0cfb5fc55",
|
||||||
|
"shasum": "",
|
||||||
|
"mirrors": [
|
||||||
|
{
|
||||||
|
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||||
|
"preferred": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"illuminate/contracts": "^6.9|^7.0|^8.0",
|
||||||
|
"illuminate/database": "^6.9|^7.0|^8.0",
|
||||||
|
"illuminate/support": "^6.9|^7.0|^8.0",
|
||||||
|
"php": "^7.2|^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.0",
|
||||||
|
"orchestra/testbench": "^4.0|^5.0|^6.0",
|
||||||
|
"phpunit/phpunit": "^8.0|^9.3"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.x-dev"
|
||||||
|
},
|
||||||
|
"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": "2021-11-16T16:57:07+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/serializable-closure",
|
"name": "laravel/serializable-closure",
|
||||||
"version": "v1.0.3",
|
"version": "v1.0.3",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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',
|
||||||
|
env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
|
||||||
|
))),
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| 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. If this value is null, personal access tokens do
|
||||||
|
| not expire. This won't tweak the lifetime of first-party sessions.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'expiration' => null,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| 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' => [
|
||||||
|
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
|
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreatePersonalAccessTokensTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
||||||
|
$table->bigIncrements('id');
|
||||||
|
$table->morphs('tokenable');
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('token', 64)->unique();
|
||||||
|
$table->text('abilities')->nullable();
|
||||||
|
$table->timestamp('last_used_at')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('personal_access_tokens');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
{
|
{
|
||||||
":resource not found": ":resource 未找到"
|
":resource not found": ":resource 未找到",
|
||||||
|
"Invalid invitation code": "无效的邀请码",
|
||||||
|
"Registration failed, please try again": "注册失败,请重试"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::group([
|
||||||
|
'prefix' => 'v1',
|
||||||
|
], function () {
|
||||||
|
foreach (File::allFiles(base_path('routes/api/v1')) as $file) {
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\Api\V1\Auth\RegisterController;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
Route::post('register', RegisterController::class);
|
||||||
Loading…
Reference in New Issue