First Commit (API Project)
This commit is contained in:
39
app/Actions/Fortify/CreateNewUser.php
Normal file
39
app/Actions/Fortify/CreateNewUser.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Fortify\Contracts\CreatesNewUsers;
|
||||
use Laravel\Jetstream\Jetstream;
|
||||
|
||||
class CreateNewUser implements CreatesNewUsers
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and create a newly registered user.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
public function create(array $input): User
|
||||
{
|
||||
Validator::make($input, [
|
||||
'USER_ID' => ['required', 'string', 'max:30', 'unique:SITE_USER_INFO,USER_ID'],
|
||||
'USER_PWD' => ['required', 'string', 'min:8'],
|
||||
'USER_EMAIL' => ['nullable', 'string', 'email', 'max:40'],
|
||||
'USER_NCNM' => ['nullable', 'string', 'max:50'],
|
||||
])->validate();
|
||||
|
||||
return User::create([
|
||||
'USER_ID' => $input['USER_ID'],
|
||||
'USER_PWD' => Hash::make($input['USER_PWD']), // 비밀번호 암호화 저장
|
||||
'USER_NCNM' => $input['USER_NCNM'] ?? null,
|
||||
'USER_EMAIL' => $input['USER_EMAIL'] ?? null,
|
||||
'USER_HP' => $input['USER_HP'] ?? null,
|
||||
'USER_STATUS' => '01', // 기본적으로 활성화 상태
|
||||
'REG_DTTM' => now(), // 등록 날짜 자동 설정
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
app/Actions/Fortify/PasswordValidationRules.php
Normal file
18
app/Actions/Fortify/PasswordValidationRules.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
trait PasswordValidationRules
|
||||
{
|
||||
/**
|
||||
* Get the validation rules used to validate passwords.
|
||||
*
|
||||
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
|
||||
*/
|
||||
protected function passwordRules(): array
|
||||
{
|
||||
return ['required', 'string', Password::default(), 'confirmed'];
|
||||
}
|
||||
}
|
||||
29
app/Actions/Fortify/ResetUserPassword.php
Normal file
29
app/Actions/Fortify/ResetUserPassword.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Fortify\Contracts\ResetsUserPasswords;
|
||||
|
||||
class ResetUserPassword implements ResetsUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and reset the user's forgotten password.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
public function reset(User $user, array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'password' => $this->passwordRules(),
|
||||
])->validate();
|
||||
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($input['password']),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
32
app/Actions/Fortify/UpdateUserPassword.php
Normal file
32
app/Actions/Fortify/UpdateUserPassword.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
|
||||
|
||||
class UpdateUserPassword implements UpdatesUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and update the user's password.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
public function update(User $user, array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'current_password' => ['required', 'string', 'current_password:web'],
|
||||
'password' => $this->passwordRules(),
|
||||
], [
|
||||
'current_password.current_password' => __('The provided password does not match your current password.'),
|
||||
])->validateWithBag('updatePassword');
|
||||
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($input['password']),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
56
app/Actions/Fortify/UpdateUserProfileInformation.php
Normal file
56
app/Actions/Fortify/UpdateUserProfileInformation.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
|
||||
|
||||
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
||||
{
|
||||
/**
|
||||
* Validate and update the given user's profile information.
|
||||
*
|
||||
* @param array<string, mixed> $input
|
||||
*/
|
||||
public function update(User $user, array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
|
||||
'photo' => ['nullable', 'mimes:jpg,jpeg,png', 'max:1024'],
|
||||
])->validateWithBag('updateProfileInformation');
|
||||
|
||||
if (isset($input['photo'])) {
|
||||
$user->updateProfilePhoto($input['photo']);
|
||||
}
|
||||
|
||||
if ($input['email'] !== $user->email &&
|
||||
$user instanceof MustVerifyEmail) {
|
||||
$this->updateVerifiedUser($user, $input);
|
||||
} else {
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given verified user's profile information.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*/
|
||||
protected function updateVerifiedUser(User $user, array $input): void
|
||||
{
|
||||
$user->forceFill([
|
||||
'name' => $input['name'],
|
||||
'email' => $input['email'],
|
||||
'email_verified_at' => null,
|
||||
])->save();
|
||||
|
||||
$user->sendEmailVerificationNotification();
|
||||
}
|
||||
}
|
||||
19
app/Actions/Jetstream/DeleteUser.php
Normal file
19
app/Actions/Jetstream/DeleteUser.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Jetstream;
|
||||
|
||||
use App\Models\User;
|
||||
use Laravel\Jetstream\Contracts\DeletesUsers;
|
||||
|
||||
class DeleteUser implements DeletesUsers
|
||||
{
|
||||
/**
|
||||
* Delete the given user.
|
||||
*/
|
||||
public function delete(User $user): void
|
||||
{
|
||||
$user->deleteProfilePhoto();
|
||||
$user->tokens->each->delete();
|
||||
$user->delete();
|
||||
}
|
||||
}
|
||||
131
app/Helpers/ApiResponse.php
Normal file
131
app/Helpers/ApiResponse.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class ApiResponse
|
||||
{
|
||||
|
||||
function normalizeFiles(array $laravelFiles): array {
|
||||
$files = ['name' => [], 'type' => [], 'tmp_name' => [], 'size' => [], 'fileType' => []];
|
||||
foreach ($laravelFiles as $file) {
|
||||
$files['name'][] = $file->getClientOriginalName();
|
||||
$files['type'][] = $file->getClientMimeType();
|
||||
$files['tmp_name'][] = $file->getPathname();
|
||||
$files['size'][] = $file->getSize();
|
||||
$files['fileType'][] = '';
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
# DebugQuery Helper
|
||||
public static function debugQueryLog(): array
|
||||
{
|
||||
$logs = DB::getQueryLog();
|
||||
|
||||
return collect($logs)->map(function ($log) {
|
||||
$query = $log['query'];
|
||||
foreach ($log['bindings'] as $binding) {
|
||||
$binding = is_numeric($binding) ? $binding : "'" . addslashes($binding) . "'";
|
||||
$query = preg_replace('/\\?/', $binding, $query, 1);
|
||||
}
|
||||
|
||||
// \n 제거
|
||||
$query = str_replace(["\n", "\r"], ' ', $query);
|
||||
return trim($query);
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
# ApiResponse Helper
|
||||
public static function success(
|
||||
$data = null,
|
||||
string $message = '요청 성공',
|
||||
array $debug = []
|
||||
): JsonResponse {
|
||||
$response = [
|
||||
'success' => true,
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
];
|
||||
if(!empty($debug)) $response['query'] = $debug;
|
||||
return response()->json($response);
|
||||
}
|
||||
|
||||
public static function error(
|
||||
string $message = '요청 실패',
|
||||
int $code = 400,
|
||||
array $error = []
|
||||
): JsonResponse {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => $message,
|
||||
'error' => [
|
||||
'code' => $code,
|
||||
'details' => $error['details'] ?? null,
|
||||
],
|
||||
], $code);
|
||||
}
|
||||
|
||||
public static function validate(
|
||||
bool $condition,
|
||||
string $message = 'Validation failed',
|
||||
int $code = 422,
|
||||
array $extra = []
|
||||
): ?JsonResponse {
|
||||
return $condition ? null : self::error($message, $code, $extra);
|
||||
}
|
||||
|
||||
public static function response($type = '', $query = '', $debug = false, $key = ''): array
|
||||
{
|
||||
if ($debug && $type != 'success') DB::enableQueryLog(); // 쿼리 추적
|
||||
|
||||
$result = match ($type) {
|
||||
'get' => $key ? $query->get()->keyBy($key) : $query->get(),
|
||||
'getSub' => $query->get(),
|
||||
'count' => $query->count(),
|
||||
'first' => $query->first(),
|
||||
'success' => 'Success',
|
||||
'result' => $query,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if($type=='getSub'){
|
||||
$array = $result->map(function ($item) {
|
||||
return (array) $item;
|
||||
})->toArray();
|
||||
foreach ($array as $row) {
|
||||
$data[$row[$key]][] = $row;
|
||||
}
|
||||
$result = $data ?? [];
|
||||
}
|
||||
|
||||
$response['data'] = $result;
|
||||
$response['query'] = ($debug) ? self::debugQueryLog() : [];
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function handle(
|
||||
callable $callback,
|
||||
string $successMessage = '요청 성공',
|
||||
string $errorMessage = '요청 실패'
|
||||
): JsonResponse {
|
||||
try {
|
||||
$result = $callback();
|
||||
|
||||
if ($result instanceof JsonResponse) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return self::success(
|
||||
$result['data'] ?? null, $successMessage, $result['query'] ?? []
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
return self::error($errorMessage, 500, [
|
||||
'details' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
app/Http/Controllers/Api/AdminApiController.php
Normal file
37
app/Http/Controllers/Api/AdminApiController.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Models\SiteAdmin;
|
||||
|
||||
class AdminApiController extends Controller
|
||||
{
|
||||
|
||||
/* /**
|
||||
* @OA\Post(
|
||||
* path="/api/admin/list",
|
||||
* summary="관리자 리스트",
|
||||
* tags={"Admin"},
|
||||
* security={{"ApiKeyAuth":{}}},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"user_token"},
|
||||
* @OA\Property(property="user_token", type="string", example="XXXXXXX")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=200, description="성공"),
|
||||
* @OA\Response(response=401, description="실패")
|
||||
* )
|
||||
*/
|
||||
public function list(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
$admins = new SiteAdmin;
|
||||
return ApiResponse::response('get', $admins, $request->debug);
|
||||
}, '관리자 목록 조회 성공', '관리자 목록 조회 실패');
|
||||
}
|
||||
}
|
||||
144
app/Http/Controllers/Api/ApiController.php
Normal file
144
app/Http/Controllers/Api/ApiController.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Member;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Info(
|
||||
* version="1.0.0",
|
||||
* title="SAM API Documentation",
|
||||
* description="SAM(Semi-Automatics Management) API 입니다.",
|
||||
* @OA\Contact(
|
||||
* email="shine1324@gmail.com"
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* @OA\Server(
|
||||
* url="https://api.5130.co.kr",
|
||||
* description="SAM API 서버"
|
||||
* )
|
||||
*/
|
||||
class ApiController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/debug-apikey",
|
||||
* tags={"API Key 인증"},
|
||||
* summary="API Key 인증 확인",
|
||||
* security={{"ApiKeyAuth":{}}},
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="API Key 인증 성공"
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=401,
|
||||
* description="인증 실패"
|
||||
* )
|
||||
* )
|
||||
*/
|
||||
public function debugApikey()
|
||||
{
|
||||
return response()->json(['message' => 'API Key 인증 성공']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/login",
|
||||
* summary="회원 토큰 정보확인",
|
||||
* tags={"Auth"},
|
||||
* @OA\RequestBody(
|
||||
* required=true,
|
||||
* @OA\JsonContent(
|
||||
* required={"USER_ID", "USER_PWD"},
|
||||
* @OA\Property(property="USER_ID", type="string", example="admin"),
|
||||
* @OA\Property(property="USER_PWD", type="string", example="1234")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="로그인 성공",
|
||||
* @OA\JsonContent(
|
||||
* @OA\Property(property="message", type="string"),
|
||||
* @OA\Property(property="USER_TOKEN", type="string")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(response=401, description="로그인 실패")
|
||||
* )
|
||||
*/
|
||||
public function login(Request $request)
|
||||
{
|
||||
$userId = $request->input('user_id');
|
||||
$userPwd = $request->input('user_pwd');
|
||||
|
||||
if (!$userId || !$userPwd) {
|
||||
return response()->json(['error' => '아이디 또는 비밀번호 누락'], 400);
|
||||
}
|
||||
|
||||
|
||||
$user = Member::where('mb_id', $userId)->first();
|
||||
|
||||
if (!$user) {
|
||||
return response()->json(['error' => '사용자를 찾을 수 없습니다.'], 404);
|
||||
}
|
||||
|
||||
$isValid = false;
|
||||
|
||||
if (Str::startsWith($user->mb_pass, '$2y$')) {
|
||||
// bcrypt로 해싱된 경우
|
||||
$isValid = Hash::check($userPwd, $user->mb_pass);
|
||||
} else {
|
||||
// sha256으로 해싱된 경우
|
||||
$isValid = strtoupper(hash('sha256', $userPwd)) === strtoupper($user->mb_pass);
|
||||
}
|
||||
|
||||
if (!$isValid) {
|
||||
return response()->json(['error' => '아이디 또는 비밀번호가 올바르지 않습니다.'], 401);
|
||||
}
|
||||
|
||||
// 선택: DB에 신규 token 저장
|
||||
$USER_TOKEN = hash('sha256', $user->mb_id.date('YmdHis'));
|
||||
$user->remember_token = $USER_TOKEN;
|
||||
$user->save();
|
||||
|
||||
return response()->json([
|
||||
'message' => '로그인 성공',
|
||||
'USER_TOKEN' => $user->USER_TOKEN,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Post(
|
||||
* path="/api/logout",
|
||||
* summary="로그아웃 (Access 및 Token 무효화)",
|
||||
* tags={"Auth"},
|
||||
* security={{"ApiKeyAuth":{}}},
|
||||
* @OA\Response(response=200, description="로그아웃 성공"),
|
||||
* @OA\Response(response=401, description="인증 실패")
|
||||
* )
|
||||
*/
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$token = $request->header('X-API-KEY'); // 또는 Authorization 헤더
|
||||
|
||||
// 회원 테이블에서 해당 토큰으로 유저 찾기
|
||||
$user = User::where('remember_token', $token)->first();
|
||||
|
||||
if ($user) {
|
||||
$user->USER_TOKEN = null;
|
||||
$user->save();
|
||||
}
|
||||
|
||||
return response()->json(['message' => '로그아웃 완료']);
|
||||
}
|
||||
|
||||
}
|
||||
16
app/Http/Controllers/Api/CommonController.php
Normal file
16
app/Http/Controllers/Api/CommonController.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CommonController
|
||||
{
|
||||
public static function getComeCode()
|
||||
{
|
||||
$query = DB::table('COM_CODE')
|
||||
->select(['CODE_TP_ID', 'CODE_ID', 'CODE_VAL', 'CODE_DESC', 'USE_YN']);
|
||||
return ApiResponse::response('get', $query);
|
||||
}
|
||||
}
|
||||
43
app/Http/Controllers/Api/FileController.php
Normal file
43
app/Http/Controllers/Api/FileController.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\FileService;
|
||||
use App\Helpers\ApiResponse;
|
||||
|
||||
class FileController extends Controller
|
||||
{
|
||||
// 파일 업로드
|
||||
public function upload(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::uploadFiles($request->all());
|
||||
}, '파일 업로드 성공', '파일 업로드 실패');
|
||||
}
|
||||
|
||||
// 파일 목록 조회
|
||||
public function list(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::getFiles($request->all());
|
||||
}, '파일 목록조회 성공', '파일 목록조회 실패');
|
||||
}
|
||||
|
||||
// 파일 삭제
|
||||
public function delete(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::deleteFiles($request->all());
|
||||
}, '파일 삭제 성공', '파일 삭제 실패');
|
||||
}
|
||||
|
||||
// 파일 정보 조회 (단건)
|
||||
public function findFile(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return FileService::findFile($request->all());
|
||||
}, '파일 정보 조회 성공', '파일 정보 조회 실패');
|
||||
}
|
||||
}
|
||||
235
app/Http/Controllers/Api/MemberController.php
Normal file
235
app/Http/Controllers/Api/MemberController.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\MemberService;
|
||||
use App\Helpers\ApiResponse;
|
||||
|
||||
class MemberController extends Controller
|
||||
{
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/member/index",
|
||||
* summary="회원 목록 조회",
|
||||
* description="회원 목록을 조회합니다.",
|
||||
* tags={"Member"},
|
||||
* security={{"ApiKeyAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(
|
||||
* name="user_token",
|
||||
* in="query",
|
||||
* required=true,
|
||||
* description="회원 인증용 토큰",
|
||||
* @OA\Schema(type="string", example="abc123token")
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="type",
|
||||
* in="query",
|
||||
* required=false,
|
||||
* description="조회 타입: 기본(default), 상세(info)",
|
||||
* @OA\Schema(type="string", enum={"default", "info"}, example="default")
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="debug",
|
||||
* in="query",
|
||||
* required=false,
|
||||
* description="디버그 모드 여부 (쿼리 로그 포함 여부)",
|
||||
* @OA\Schema(type="boolean", example=true)
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="status",
|
||||
* in="query",
|
||||
* required=false,
|
||||
* description="상태 필터링 (01,02,03 사용자만 조회)",
|
||||
* @OA\Schema(type="boolean", example=true)
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="회원 목록 조회 성공",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="회원목록 조회 성공"),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* type="object",
|
||||
* @OA\Property(
|
||||
* property="1",
|
||||
* type="object",
|
||||
* @OA\Property(property="mb_num", type="integer", example=1),
|
||||
* @OA\Property(property="tn_num", type="string", example=null),
|
||||
* @OA\Property(property="mb_id", type="string", example="admin"),
|
||||
* @OA\Property(property="mb_name", type="string", example="권혁성"),
|
||||
* @OA\Property(property="mb_phone", type="string", example="010-4820-9104"),
|
||||
* @OA\Property(property="mb_mail", type="string", example="shine1324@gmail.com"),
|
||||
* @OA\Property(property="email_verified_at", type="string", format="date-time", example=null),
|
||||
* @OA\Property(property="mb_type", type="string", example=null),
|
||||
* @OA\Property(property="mb_level", type="integer", example=1),
|
||||
* @OA\Property(property="last_login", type="string", format="date-time", example=null),
|
||||
* @OA\Property(property="reg_date", type="string", format="date-time", example="2025-07-16T09:28:41.000000Z"),
|
||||
* @OA\Property(property="created_at", type="string", format="date-time", example=null),
|
||||
* @OA\Property(property="updated_at", type="string", format="date-time", example="2025-07-16T09:30:56.000000Z")
|
||||
* )
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="query",
|
||||
* type="array",
|
||||
* @OA\Items(type="string", example="select * from `members`")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=401, description="인증 실패"),
|
||||
* @OA\Response(response=403, description="권한 없음")
|
||||
* )
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
try {
|
||||
|
||||
$type = $request->input('type', 'default');
|
||||
$userToken = $request->input('user_token', '');
|
||||
$debug = $request->boolean('debug', false);
|
||||
$result = MemberService::getMembers($userToken, $type, $debug);
|
||||
|
||||
return ApiResponse::success($result['data'], '회원목록 조회 성공',$result['query']);
|
||||
} catch (\Throwable $e) {
|
||||
return ApiResponse::error('회원목록 조회 실패', 500, [
|
||||
'details' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($request) {
|
||||
return MemberService::setMember($request->all());
|
||||
}, '회원등록 성공', '회원등록 실패');
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get (
|
||||
* path="/api/member/show/{user_no}",
|
||||
* summary="회원 상세조회",
|
||||
* description="user_no 기준으로 회원 상세 정보를 조회합니다.",
|
||||
* tags={"Member"},
|
||||
* security={{"ApiKeyAuth":{}}},
|
||||
*
|
||||
* @OA\Parameter(
|
||||
* name="user_no",
|
||||
* in="path",
|
||||
* required=true,
|
||||
* description="회원 번호 (USER_NO)",
|
||||
* @OA\Schema(type="integer", example=1)
|
||||
* ),
|
||||
* @OA\Parameter(
|
||||
* name="debug",
|
||||
* in="query",
|
||||
* required=false,
|
||||
* description="디버그 모드 여부 (쿼리 확인용)",
|
||||
* @OA\Schema(type="boolean", example=true)
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="조회 성공",
|
||||
* @OA\JsonContent(
|
||||
* type="object",
|
||||
* @OA\Property(property="success", type="boolean", example=true),
|
||||
* @OA\Property(property="message", type="string", example="회원 상세조회 성공"),
|
||||
* @OA\Property(
|
||||
* property="data",
|
||||
* type="object",
|
||||
* @OA\Property(property="mb_num", type="integer", example=1),
|
||||
* @OA\Property(property="tn_num", type="string", example=null),
|
||||
* @OA\Property(property="mb_id", type="string", example="admin"),
|
||||
* @OA\Property(property="mb_name", type="string", example="권혁성"),
|
||||
* @OA\Property(property="mb_phone", type="string", example="010-4820-9104"),
|
||||
* @OA\Property(property="mb_mail", type="string", example="shine1324@gmail.com"),
|
||||
* @OA\Property(property="email_verified_at", type="string", format="date-time", example=null),
|
||||
* @OA\Property(property="mb_type", type="string", example=null),
|
||||
* @OA\Property(property="mb_level", type="integer", example=1),
|
||||
* @OA\Property(property="last_login", type="string", format="date-time", example=null),
|
||||
* @OA\Property(property="reg_date", type="string", format="date-time", example="2025-07-16T09:28:41.000000Z"),
|
||||
* @OA\Property(property="created_at", type="string", format="date-time", example=null),
|
||||
* @OA\Property(property="updated_at", type="string", format="date-time", example="2025-07-16T09:30:56.000000Z")
|
||||
* ),
|
||||
* @OA\Property(
|
||||
* property="query",
|
||||
* type="array",
|
||||
* @OA\Items(type="string", example="select * from `members` where `mb_num` = 1 limit 1")
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
*
|
||||
* @OA\Response(response=404, description="회원 정보 없음"),
|
||||
* @OA\Response(response=401, description="인증 실패")
|
||||
* )
|
||||
*/
|
||||
public function show(Request $request, $userNo)
|
||||
{
|
||||
try {
|
||||
|
||||
$debug = $request->boolean('debug', false);
|
||||
$result = MemberService::getMember($userNo, $debug);
|
||||
|
||||
return ApiResponse::success($result['data'], '회원 상세조회 성공',$result['query']);
|
||||
} catch (\Throwable $e) {
|
||||
return ApiResponse::error('회원 상세조회 실패', 500, [
|
||||
'details' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, string $id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function delAdmin($userNo, Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($userNo, $request) {
|
||||
return MemberService::delAdmin($userNo);
|
||||
}, '관리자 제외 성공', '관리자 제외 실패');
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 설정
|
||||
*/
|
||||
public function setAdmin($userNo, Request $request)
|
||||
{
|
||||
return ApiResponse::handle(function () use ($userNo, $request) {
|
||||
return MemberService::setAdmin($userNo);
|
||||
}, '관리자 설정 성공', '관리자 설정 실패');
|
||||
}
|
||||
}
|
||||
|
||||
8
app/Http/Controllers/Controller.php
Normal file
8
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
28
app/Http/Kernel.php
Normal file
28
app/Http/Kernel.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* 전역 미들웨어
|
||||
*/
|
||||
protected $middleware = [
|
||||
\App\Http\Middleware\CorsMiddleware::class, // CORS 미들웨어 추가
|
||||
];
|
||||
|
||||
/**
|
||||
* 웹 미들웨어 그룹
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [],
|
||||
'api' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* 개별 미들웨어 설정
|
||||
*/
|
||||
protected $routeMiddleware = [];
|
||||
}
|
||||
50
app/Http/Middleware/ApiKeyMiddleware.php
Normal file
50
app/Http/Middleware/ApiKeyMiddleware.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\User;
|
||||
|
||||
class ApiKeyMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$apiKey = $request->header('X-API-KEY');
|
||||
|
||||
$validApiKey = false;
|
||||
|
||||
// 1. API 키가 유효한지 확인
|
||||
if ($apiKey) {
|
||||
$validApiKey = DB::table('api_keys')
|
||||
->where('key', $apiKey)
|
||||
->where('is_active', true)
|
||||
->exists();
|
||||
|
||||
// 2. 회원 인증 (remember_token으로)
|
||||
if (!$validApiKey) {
|
||||
$user = User::where('remember_token', $apiKey)->first();
|
||||
|
||||
if ($user) {
|
||||
$validApiKey = true;
|
||||
|
||||
// ✅ 세션에 유저 정보 저장
|
||||
session(['Adm' => [
|
||||
'idx' => $user->mb_num,
|
||||
'id' => $user->mb_id,
|
||||
'name' => $user->mb_name,
|
||||
'level' => $user->mb_level,
|
||||
'token' => $user->remember_token,
|
||||
]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$validApiKey) {
|
||||
return response()->json(['message' => 'Unauthorized. Invalid or missing API key or token'], 401);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
27
app/Http/Middleware/CheckPermission.php
Normal file
27
app/Http/Middleware/CheckPermission.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Services\AdminPermissionService;
|
||||
|
||||
class CheckPermission
|
||||
{
|
||||
public function handle(Request $request, Closure $next, string $permissionCode)
|
||||
{
|
||||
$userToken = $request->input('user_token');
|
||||
if (!$userToken) {
|
||||
$userToken = $request->header('X-API-KEY');
|
||||
if (!$userToken) {
|
||||
return response()->json(['error' => '토큰이 없습니다.'], 401);
|
||||
}
|
||||
}
|
||||
|
||||
if (!AdminPermissionService::hasPermission($userToken, $permissionCode)) {
|
||||
return response()->json(['error' => '권한이 없습니다.'], 403);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
34
app/Http/Middleware/CheckSwaggerAuth.php
Normal file
34
app/Http/Middleware/CheckSwaggerAuth.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use App\Models\Member;
|
||||
|
||||
class CheckSwaggerAuth
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$token = Session::get('USER_TOKEN');
|
||||
|
||||
if (!$token) {
|
||||
// 원래 URL 저장 후 로그인 페이지로 이동
|
||||
Session::put('redirect_to', $request->fullUrl());
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
$user = Member::where('remember_token', $token)->first();
|
||||
|
||||
if (!$user) {
|
||||
Session::forget('USER_TOKEN');
|
||||
Session::forget('USER_ID');
|
||||
|
||||
Session::put('redirect_to', $request->fullUrl());
|
||||
return redirect()->route('login');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
25
app/Http/Middleware/CorsMiddleware.php
Normal file
25
app/Http/Middleware/CorsMiddleware.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class CorsMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$response = $next($request);
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Origin', '*');
|
||||
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
$response->headers->set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||
|
||||
if ($request->isMethod('OPTIONS')) {
|
||||
return response()->json([], 200, $response->headers->all());
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
45
app/Http/Responses/CustomLoginResponse.php
Normal file
45
app/Http/Responses/CustomLoginResponse.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Responses;
|
||||
|
||||
use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use App\Models\User;
|
||||
|
||||
class CustomLoginResponse implements LoginResponseContract
|
||||
{
|
||||
public function toResponse($request)
|
||||
{
|
||||
$user = auth('web')->user();
|
||||
|
||||
if (!$user) {
|
||||
abort(500, '로그인 유저 정보를 가져올 수 없습니다.');
|
||||
}
|
||||
|
||||
//TOKEN 설정
|
||||
$token = $user->remember_token;
|
||||
if(!$token || substr($user->reg_date,0,10) < date('Y-m-d', strtotime('-15 day'))) {
|
||||
$token = hash('sha256', $user->mb_id . now()->format('YmdHis'));
|
||||
User::where('USER_NO', $user->mb_num)->update(['USER_TOKEN' => $token]);
|
||||
}
|
||||
|
||||
// ✅ 세션에 유저 정보 저장
|
||||
session(['Adm' => [
|
||||
'idx' => $user->USER_NO,
|
||||
'id' => $user->USER_ID,
|
||||
'name' => $user->USER_NCNM,
|
||||
'level' => $user->LEVEL,
|
||||
'part' => $user->USER_PART,
|
||||
'dept' => $user->USER_DEPT,
|
||||
'token' => $token,
|
||||
]]);
|
||||
|
||||
Session::put('USER_TOKEN', $token);
|
||||
Session::put('USER_ID', $user->mb_id);
|
||||
|
||||
$redirectTo = session('redirect_to', route('dashboard'));
|
||||
session()->forget('redirect_to');
|
||||
|
||||
return redirect()->to($redirectTo);
|
||||
}
|
||||
}
|
||||
10
app/Models/ApiKey.php
Normal file
10
app/Models/ApiKey.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ApiKey extends Model
|
||||
{
|
||||
protected $fillable = ['key', 'description', 'is_active'];
|
||||
}
|
||||
39
app/Models/Member.php
Normal file
39
app/Models/Member.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
class Member extends Authenticatable
|
||||
{
|
||||
use HasApiTokens, Notifiable, TwoFactorAuthenticatable;
|
||||
|
||||
protected $primaryKey = 'mb_id'; // 기본 키 변경
|
||||
|
||||
protected $fillable = [
|
||||
'mb_id', 'mb_pass', 'mb_name', 'mb_phone', 'mb_mail',
|
||||
'email_verified_at', 'mb_type', 'mb_level', 'last_login',
|
||||
'reg_date', 'remember_token'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'mb_pass', 'remember_token',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'reg_date' => 'datetime',
|
||||
];
|
||||
|
||||
public function getAuthPassword()
|
||||
{
|
||||
return $this->mb_pass; // 기본 비밀번호 필드를 mb_pass로 설정
|
||||
}
|
||||
|
||||
public function getAuthIdentifierName()
|
||||
{
|
||||
return 'mb_id'; // 기본 로그인 필드를 mb_id로 변경
|
||||
}
|
||||
}
|
||||
19
app/Models/SiteAdmin.php
Normal file
19
app/Models/SiteAdmin.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\UppercaseAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SiteAdmin extends Model
|
||||
{
|
||||
use UppercaseAttributes; // 테이블 컬럼명 대문자 처리
|
||||
|
||||
protected $table = 'SITE_ADMIN';
|
||||
protected $primaryKey = 'UNO';
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'UNO', 'LEVEL'. 'COMMENT'
|
||||
];
|
||||
}
|
||||
42
app/Models/User.php
Normal file
42
app/Models/User.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Fortify\TwoFactorAuthenticatable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use App\Traits\UppercaseAttributes;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasApiTokens, Notifiable, TwoFactorAuthenticatable;
|
||||
|
||||
protected $table = 'members'; // 테이블 이름 변경
|
||||
|
||||
protected $primaryKey = 'mb_id'; // 기본 키 변경
|
||||
|
||||
protected $fillable = [
|
||||
'mb_id', 'mb_pass', 'mb_name', 'mb_phone', 'mb_mail',
|
||||
'email_verified_at', 'mb_type', 'mb_level', 'last_login',
|
||||
'reg_date', 'remember_token'
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'mb_pass', 'remember_token',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'reg_date' => 'datetime',
|
||||
];
|
||||
|
||||
public function getAuthPassword()
|
||||
{
|
||||
return $this->mb_pass; // 기본 비밀번호 필드를 mb_pass로 설정
|
||||
}
|
||||
|
||||
public function getAuthIdentifierName()
|
||||
{
|
||||
return 'mb_id'; // 기본 로그인 필드를 mb_id로 변경
|
||||
}
|
||||
}
|
||||
24
app/Providers/AppServiceProvider.php
Normal file
24
app/Providers/AppServiceProvider.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
78
app/Providers/FortifyServiceProvider.php
Normal file
78
app/Providers/FortifyServiceProvider.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Actions\Fortify\CreateNewUser;
|
||||
use App\Actions\Fortify\ResetUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserPassword;
|
||||
use App\Actions\Fortify\UpdateUserProfileInformation;
|
||||
use App\Models\Member;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Fortify\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use Laravel\Fortify\Contracts\LoginResponse;
|
||||
use App\Http\Responses\CustomLoginResponse;
|
||||
|
||||
class FortifyServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
// ✅ 커스텀 로그인 응답 등록
|
||||
$this->app->singleton(LoginResponse::class, CustomLoginResponse::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
|
||||
Fortify::createUsersUsing(CreateNewUser::class);
|
||||
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
|
||||
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
|
||||
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
|
||||
|
||||
// ✅ 로그인 시 `USER_ID`를 사용하도록 변경
|
||||
Fortify::authenticateUsing(function (Request $request) {
|
||||
|
||||
$user = Member::where('mb_id', $request->USER_ID)->first();
|
||||
if(!$user) return null;
|
||||
|
||||
// 기존 sha256 방식 확인
|
||||
if ($user && strtoupper(hash('sha256', $request->password)) === $user->mb_pass) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
// bcrypt 방식으로 저장된 사용자 로그인 처리
|
||||
else if (Hash::check($request->password, $user->mb_pass)) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
Fortify::loginView(fn() => view('auth.login')); // 로그인 페이지 지정
|
||||
|
||||
RateLimiter::for('login', function (Request $request) {
|
||||
$throttleKey = Str::transliterate(Str::lower($request->input('USER_ID')).'|'.$request->ip());
|
||||
|
||||
return Limit::perMinute(5)->by($throttleKey);
|
||||
});
|
||||
|
||||
RateLimiter::for('two-factor', function (Request $request) {
|
||||
return Limit::perMinute(5)->by($request->session()->get('login.id'));
|
||||
});
|
||||
}
|
||||
}
|
||||
43
app/Providers/JetstreamServiceProvider.php
Normal file
43
app/Providers/JetstreamServiceProvider.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Actions\Jetstream\DeleteUser;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Laravel\Jetstream\Jetstream;
|
||||
|
||||
class JetstreamServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->configurePermissions();
|
||||
|
||||
Jetstream::deleteUsersUsing(DeleteUser::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the permissions that are available within the application.
|
||||
*/
|
||||
protected function configurePermissions(): void
|
||||
{
|
||||
Jetstream::defaultApiTokenPermissions(['read']);
|
||||
|
||||
Jetstream::permissions([
|
||||
'create',
|
||||
'read',
|
||||
'update',
|
||||
'delete',
|
||||
]);
|
||||
}
|
||||
}
|
||||
38
app/Services/AdminPermissionService.php
Normal file
38
app/Services/AdminPermissionService.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\User;
|
||||
use App\Models\SiteAdmin;
|
||||
|
||||
class AdminPermissionService
|
||||
{
|
||||
public static function getPermissionsByToken(string $userToken): array
|
||||
{
|
||||
$user = User::where('USER_TOKEN', $userToken)
|
||||
->first();
|
||||
|
||||
if (!$user) return [];
|
||||
|
||||
$admin = SiteAdmin::where('UNO', $user->USER_NO)
|
||||
->first();
|
||||
|
||||
if (!$admin) return [];
|
||||
|
||||
$permissionCodes = DB::table('SITE_ADMIN_USER_ROLE AS ur')
|
||||
->join('SITE_ADMIN_ROLE_PERMISSION AS rp', 'ur.ROLE_ID', '=', 'rp.ROLE_ID')
|
||||
->join('SITE_ADMIN_PERMISSIONS AS p', 'rp.PERMISSION_ID', '=', 'p.ID')
|
||||
->where('ur.USER_ID', $admin->A_IDX)
|
||||
->pluck('p.CODE')
|
||||
->toArray();
|
||||
|
||||
return $permissionCodes;
|
||||
}
|
||||
|
||||
public static function hasPermission(string $userToken, string $code): bool
|
||||
{
|
||||
$permissions = self::getPermissionsByToken($userToken);
|
||||
return in_array($code, $permissions);
|
||||
}
|
||||
}
|
||||
188
app/Services/FileService.php
Normal file
188
app/Services/FileService.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class FileService
|
||||
{
|
||||
public static function saveFiles($files, string $table, string $t_id, string $t_id_type = '00')
|
||||
{
|
||||
if (isset($files) && is_array($files)) {
|
||||
foreach ($files as $file) {
|
||||
$fileName = $file->getClientOriginalName();
|
||||
$randomName = bin2hex(random_bytes(16));
|
||||
$fileType = $file->getClientMimeType();
|
||||
$fileSize = $file->getSize();
|
||||
//$tempFile = $file->getPathname();
|
||||
|
||||
$folder = config('custom.data_path') . substr($t_id, 0, 6) . "/";
|
||||
$targetPath = $folder . $randomName;
|
||||
|
||||
if (!is_dir($folder)) {
|
||||
mkdir($folder, 0755, true);
|
||||
}
|
||||
|
||||
try {
|
||||
//move_uploaded_file($tempFile, $targetPath);
|
||||
$file->move($folder, $randomName);
|
||||
chmod($targetPath, 0644);
|
||||
|
||||
DB::table('SITE_FILES')->insert([
|
||||
'F_NAME' => $fileName,
|
||||
'R_NAME' => $randomName,
|
||||
'TABLE' => $table,
|
||||
'T_ID' => $t_id,
|
||||
'T_ID_TYPE' => $t_id_type,
|
||||
'F_TYPE' => $fileType,
|
||||
'F_SIZE' => $fileSize,
|
||||
'REG_USER_NO' => session('Adm.idx') ?? 1,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return ApiResponse::error('파일업로드 실패', 422);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function uploadFiles($request)
|
||||
{
|
||||
$files = $request['upload'] ?? '';
|
||||
$table = $request['table'] ?? 'COMPANY_INFO';
|
||||
$t_id = $request['com_no'];
|
||||
$file_no = $request['file_no'] ?? '';
|
||||
|
||||
if (isset($files) && is_array($files)) {
|
||||
foreach ($files as $key => $file) {
|
||||
|
||||
$originalName = $file->getClientOriginalName(); // 예: "파일명.jpg"
|
||||
$ext = pathinfo($originalName, PATHINFO_EXTENSION); // 확장자만 추출: "jpg"
|
||||
$userInputName = $request['fileName'][$key] ?? null;
|
||||
$fileName = $userInputName ? $userInputName . '.' . $ext : $originalName;
|
||||
|
||||
$randomName = bin2hex(random_bytes(16));
|
||||
$fileType = $file->getClientMimeType();
|
||||
$fileSize = $file->getSize();
|
||||
$t_id_type = $request['fileType'][$key] ?? '00';
|
||||
|
||||
$folder = config('custom.data_path') . substr($t_id, 0, 6) . "/";
|
||||
$targetPath = $folder . $randomName;
|
||||
|
||||
if (!is_dir($folder)) {
|
||||
mkdir($folder, 0755, true);
|
||||
}
|
||||
|
||||
try {
|
||||
$file->move($folder, $randomName);
|
||||
chmod($targetPath, 0644);
|
||||
|
||||
if (!empty($file_no)) {
|
||||
self::deleteFiles(['f_id' => $file_no]);
|
||||
}
|
||||
// 신규 파일 등록
|
||||
DB::table('SITE_FILES')->insert([
|
||||
'F_NAME' => $fileName,
|
||||
'R_NAME' => $randomName,
|
||||
'TABLE' => $table,
|
||||
'T_ID' => $t_id,
|
||||
'T_ID_TYPE' => $t_id_type,
|
||||
'F_TYPE' => $fileType,
|
||||
'F_SIZE' => $fileSize,
|
||||
'REG_USER_NO' => session('Adm.idx') ?? 1,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return ApiResponse::error('파일업로드 실패', 422);
|
||||
}
|
||||
}
|
||||
|
||||
}else if($file_no) {
|
||||
|
||||
// 기존파일인데 업로드 파일이 없을경우 이름과 타입만 변경
|
||||
if ($request['fileName'][1]) {
|
||||
$ext = pathinfo(DB::table('SITE_FILES')->where('F_NO', $file_no)->value('F_NAME'), PATHINFO_EXTENSION);
|
||||
$data['F_NAME'] = $request['fileName'][1] . '.' . $ext;
|
||||
}
|
||||
|
||||
if ($request['fileType'][1]) {
|
||||
$data['T_ID_TYPE'] = $request['fileType'][1];
|
||||
}
|
||||
|
||||
DB::table('SITE_FILES')->where('F_NO', $file_no)->update($data);
|
||||
}
|
||||
return self::getFiles($request);
|
||||
}
|
||||
|
||||
public static function getFiles(array $params): array
|
||||
{
|
||||
$tType = $params['t_type'] ?? null;
|
||||
$table = $params['table'] ?? '';
|
||||
$idx = $params['idx'] ?? '';
|
||||
$com_no = $params['com_no'] ?? '';
|
||||
$params['debug'] = $params['debug'] ?? true;
|
||||
|
||||
$query = DB::table('SITE_FILES as SF')
|
||||
->select('SF.T_ID', 'SF.F_NO', 'SF.F_NAME', 'SF.R_NAME', DB::raw('LEFT(SF.T_ID, 6) as PATH'), 'SF.REG_USER_NO');
|
||||
|
||||
if ($table) {
|
||||
$query->where('SF.TABLE', $table);
|
||||
}
|
||||
if ($idx) {
|
||||
$query->whereIn('SF.T_ID', explode(',', $idx));
|
||||
}else if ($com_no) {
|
||||
$query->where('SF.T_ID', $com_no);
|
||||
}
|
||||
if ($tType) {
|
||||
$query->where('SF.T_ID_TYPE', $tType);
|
||||
}
|
||||
|
||||
return ApiResponse::response('getSub', $query, $params['debug'], 'T_ID');
|
||||
}
|
||||
|
||||
public static function deleteFiles(array $params): string
|
||||
{
|
||||
$table = $params['TABLE'] ?? null;
|
||||
$t_id = $params['T_ID'] ?? null;
|
||||
$f_id = $params['f_id'] ?? null;
|
||||
|
||||
$query = DB::table('SITE_FILES');
|
||||
|
||||
if ($table && $t_id) {
|
||||
$query->where('TABLE', $table)->where('T_ID', $t_id);
|
||||
} else if ($f_id) {
|
||||
$query->whereIn('F_NO', explode(',', $f_id));
|
||||
} else {
|
||||
return 'Error';
|
||||
logger('파일삭제 - 검색조건이 없음');
|
||||
}
|
||||
|
||||
$files = $query->get();
|
||||
if (empty($files)) {
|
||||
return 'Success';
|
||||
}
|
||||
foreach ($files as $file) {
|
||||
$filePath = config('custom.data_path') . substr($file->T_ID, 0, 6) . "/" . $file->R_NAME;
|
||||
if (file_exists($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
}
|
||||
$query->delete();
|
||||
|
||||
return 'Success';
|
||||
}
|
||||
|
||||
public static function findFile(array $params): ?array
|
||||
{
|
||||
$fileName = $params['fileName'] ?? '';
|
||||
$F_NO = $params['file_no'] ?? '';
|
||||
|
||||
$query = DB::table('SITE_FILES as SF')
|
||||
->select('F_NAME', 'T_ID', 'F_TYPE', DB::raw("IFNULL((SELECT COM_NAME FROM COMPANY_INFO CI WHERE SF.T_ID = CI.COM_NO AND SF.TABLE = 'COMPANY_INFO' LIMIT 1), '') as COM_NAME"));
|
||||
|
||||
if($F_NO) $query->where('F_NO', $F_NO);
|
||||
else $query->where('R_NAME', $fileName);
|
||||
|
||||
return ApiResponse::response('first', $query, $params['debug']);
|
||||
}
|
||||
}
|
||||
|
||||
151
app/Services/MemberService.php
Normal file
151
app/Services/MemberService.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Models\Member;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class MemberService
|
||||
{
|
||||
|
||||
/**
|
||||
* 회원 조회(리스트)
|
||||
*/
|
||||
public static function getMembers(string $userToken, string $type = 'default', bool $debug = false, bool $status = false)
|
||||
{
|
||||
|
||||
$query = new Member();
|
||||
|
||||
return ApiResponse::response('get', $query, $debug, 'mb_num');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 단일 회원 조회
|
||||
*/
|
||||
public static function getMember(int $userNo, bool $debug = false)
|
||||
{
|
||||
|
||||
$query = Member::where('mb_num', $userNo);
|
||||
|
||||
return ApiResponse::response('first', $query, $debug);
|
||||
}
|
||||
|
||||
/**
|
||||
* 회원 등록 또는 수정
|
||||
*/
|
||||
public static function setMember(array $params)
|
||||
{
|
||||
if ($res = ApiResponse::validate(isset($params['user_id']), '아이디 없음')) return $res;
|
||||
if ($res = ApiResponse::validate(isset($params['user_ncnm']), '이름 없음')) return $res;
|
||||
|
||||
$pwd1 = $params['user_pwd1'] ?? null;
|
||||
$pwd2 = $params['user_pwd2'] ?? null;
|
||||
if ($res = ApiResponse::validate(
|
||||
!$pwd1 || $pwd1 === $pwd2,
|
||||
'비밀번호가 일치하지 않음'
|
||||
)) return $res;
|
||||
|
||||
$now = now();
|
||||
|
||||
$data = [
|
||||
'USER_EMAIL' => $params['user_email'] ?? null,
|
||||
'USER_HP' => $params['user_hp'] ?? null,
|
||||
'USER_IP' => $params['user_ip'] ?? null,
|
||||
'ALT_DTTM' => $now,
|
||||
];
|
||||
|
||||
if (!empty($params['user_start_dt'])) {
|
||||
$data['USER_START_DT'] = $params['user_start_dt'];
|
||||
}
|
||||
if (!empty($params['user_end_dt'])) {
|
||||
$data['USER_END_DT'] = $params['user_end_dt'];
|
||||
}
|
||||
|
||||
// 신규 등록
|
||||
if (empty($params['user_no'])) {
|
||||
|
||||
// 초기 비빌번호 설정이 없으면 0000 으로 셋팅
|
||||
$pwd = $pwd1 ?? '0000';
|
||||
|
||||
$data += [
|
||||
'USER_ID' => $params['user_id'],
|
||||
'USER_PWD' => hash('sha256', $pwd),
|
||||
'USER_NCNM' => $params['user_ncnm'] ?? null,
|
||||
'USER_PART' => $params['user_part'] ?? null,
|
||||
'USER_DEPT' => $params['user_dept'] ?? null,
|
||||
'USER_ROLE' => $params['user_role'] ?? null,
|
||||
'USER_STATUS' => $params['user_status'] ?? null,
|
||||
'USER_MEMO' => $params['user_memo'] ?? null,
|
||||
'REG_DTTM' => $now,
|
||||
'ALT_DTTM' => $now,
|
||||
];
|
||||
|
||||
DB::table('SITE_USER_INFO')->insert($data);
|
||||
}
|
||||
|
||||
// 수정
|
||||
else {
|
||||
if (!empty($pwd1)) {
|
||||
$data['USER_PWD'] = hash('sha256', $pwd1);
|
||||
}
|
||||
|
||||
if (AdminPermissionService::hasPermission(session('Adm.token'), 'AC')) {
|
||||
$data += [
|
||||
'USER_ID' => $params['user_id'],
|
||||
'USER_NCNM' => $params['user_ncnm'],
|
||||
'USER_PART' => $params['user_part'],
|
||||
'USER_DEPT' => $params['user_dept'],
|
||||
'USER_ROLE' => $params['user_role'],
|
||||
'USER_STATUS' => $params['user_status'],
|
||||
'USER_MEMO' => $params['user_memo'],
|
||||
'ALT_DTTM' => $now,
|
||||
];
|
||||
}
|
||||
|
||||
DB::table('SITE_USER_INFO')
|
||||
->where('USER_NO', $params['user_no'])
|
||||
->update($data);
|
||||
}
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 권한 삭제
|
||||
*/
|
||||
public static function delAdmin(int $userNo)
|
||||
{
|
||||
DB::table('SITE_ADMIN')->where('UNO', $userNo)->delete();
|
||||
DB::table('SITE_USER_INFO')
|
||||
->where('USER_NO', $userNo)
|
||||
->update(['USER_STATUS' => '02']);
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
|
||||
/**
|
||||
* 관리자 권한 등록
|
||||
*/
|
||||
public static function setAdmin(int $userNo)
|
||||
{
|
||||
$mem = DB::table('SITE_USER_INFO')
|
||||
->select('USER_ROLE', 'USER_PART')
|
||||
->where('USER_NO', $userNo)
|
||||
->first();
|
||||
if (!$mem) {
|
||||
return ApiResponse::error('존재하지 않는 회원입니다.', 404);
|
||||
}
|
||||
|
||||
DB::table('SITE_ADMIN')->updateOrInsert(
|
||||
['UNO' => $userNo],
|
||||
['LEVEL' => 'public', 'COMMENT' => '일반관리자']
|
||||
);
|
||||
DB::table('SITE_USER_INFO')
|
||||
->where('USER_NO', $userNo)
|
||||
->update(['USER_STATUS' => '01']);
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
}
|
||||
24
app/Traits/UppercaseAttributes.php
Normal file
24
app/Traits/UppercaseAttributes.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
trait UppercaseAttributes
|
||||
{
|
||||
protected function getAttributeFromArray($key)
|
||||
{
|
||||
$upperKey = strtoupper($key);
|
||||
return parent::getAttributeFromArray($upperKey);
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
{
|
||||
$upperKey = strtoupper($key);
|
||||
return parent::__get($upperKey);
|
||||
}
|
||||
|
||||
public function __set($key, $value)
|
||||
{
|
||||
$upperKey = strtoupper($key);
|
||||
return parent::__set($upperKey, $value);
|
||||
}
|
||||
}
|
||||
17
app/View/Components/AppLayout.php
Normal file
17
app/View/Components/AppLayout.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AppLayout extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('layouts.app');
|
||||
}
|
||||
}
|
||||
17
app/View/Components/GuestLayout.php
Normal file
17
app/View/Components/GuestLayout.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class GuestLayout extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('layouts.guest');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user