наладил авторизацию, начал логику

This commit is contained in:
p.belezov 2024-10-31 17:48:39 +08:00
parent a31c8aed39
commit 84ab4777c2
15 changed files with 505 additions and 89 deletions

View File

@ -4,58 +4,116 @@ namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator; use App\Models\User;
class AuthController extends Controller class AuthController extends Controller
{ {
/**
* Create user
*
* @param [string] name
* @param [string] email
* @param [string] password
* @param [string] password_confirmation
* @return [string] message
*/
public function register(Request $request) public function register(Request $request)
{ {
$validator = Validator::make($request->all(), [ $request->validate([
'name' => 'required', 'name' => 'required|string',
'email' => 'required|email|unique:users', 'email'=>'required|string|unique:users',
'password' => 'required', 'password'=>'required|string',
'c_password' => 'required|same:password'
]); ]);
if ($validator->fails()) { $user = new User([
return response()->json($validator->errors(), 422); 'name' => $request->name,
}
$user = User::create([
'name' => $request->name,
'email' => $request->email, 'email' => $request->email,
'password' => bcrypt($request->password), 'password' => bcrypt($request->password),
]); ]);
$token = $user->createToken('auth_token')->plainTextToken; if($user->save()){
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->plainTextToken;
return response()->json([ return response()->json([
'user' => $user, 'message' => 'Successfully created user!',
'token' => $token, 'user' => $user,
]); 'accessToken'=> $token,
],201);
}
else{
return response()->json(['error'=>'Provide proper details']);
}
} }
/**
* Login user and create token
*
* @param [string] email
* @param [string] password
* @param [boolean] remember_me
*/
public function login(Request $request) public function login(Request $request)
{ {
if (!Auth::attempt($request->only('email', 'password'))) { $request->validate([
return response()->json(['message' => 'Invalid login details'], 401); 'email' => 'required|string|email',
'password' => 'required|string',
'remember_me' => 'boolean'
]);
$credentials = request(['email','password']);
if(!Auth::attempt($credentials))
{
return response()->json([
'message' => 'Unauthorized'
],401);
} }
$user = User::where('email', $request['email'])->firstOrFail(); $user = $request->user();
$tokenResult = $user->createToken('Personal Access Token');
$token = $user->createToken('auth_token')->plainTextToken; $token = $tokenResult->plainTextToken;
return response()->json([ return response()->json([
'accessToken' =>$token,
'user' => $user, 'user' => $user,
'token' => $token, 'token_type' => 'Bearer',
]); ]);
} }
/**
* Get the authenticated User
*
* @return [json] user object
*/
public function user(Request $request)
{
return response()->json($request->user());
}
/**
* Get the authenticated User
*
* @return [json] user object
*/
public function username(Request $request)
{
return response()->json($request->user()['name']);
}
/**
* Logout user (Revoke the token)
*
* @return [string] message
*/
public function logout(Request $request) public function logout(Request $request)
{ {
$request->user()->currentAccessToken()->delete(); $request->user()->tokens()->delete();
return response()->json([
'message' => 'Successfully logged out'
]);
return response()->json(['message' => 'Logged out']);
} }
} }

View File

@ -0,0 +1,69 @@
<?php
namespace App\Http\Controllers;
use App\Models\Computer;
use Illuminate\Http\Request;
class ComputerController extends Controller
{
public function index()
{
return Computer::select()->get();
}
public function getById(Request $request){
return Computer::find($request->get('id'));
}
public function getByUserId(Request $request){
return Computer::select()->where('user_id', '=', $request->get('user_id'))->get();
}
public function create(Request $request)
{
$request->validate([
'user_id' => 'required|exists:users,id',
'name' => 'required|string|max:256',
'cpu' => 'nullable|string|max:256',
'motherboard' => 'nullable|string|max:256',
'gpu' => 'nullable|string|max:256',
'additional_info' => 'nullable|string|max:256',
]);
$computer = Computer::create($request->all());
return response()->json($wish, 201);
}
public function update(Request $request)
{
$request->validate([
'id' => 'required|exists:computers,id',
'name' => 'required|string|max:256',
'cpu' => 'nullable|string|max:256',
'motherboard' => 'nullable|string|max:256',
'gpu' => 'nullable|string|max:256',
'additional_info' => 'nullable|string|max:256',
]);
$computer = Computer::find($request->get('id'));
$computer->name = $request->get('name');
$computer->cpu = $request->get('cpu');
$computer->motherboard = $request->get('motherboard');
$computer->gpu = $request->get('gpu');
$computer->additional_info = $request->get('additional_info');
$computer->save();
return response()->json($computer);
}
public function destroy(Request $request)
{
$request->validate([
'id' => 'required|exists:wishes,id',
]);
$destroyed = Computer::destroy($request->get('id'));
return response()->json($destroyed, 204);
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers;
use App\Models\Job;
use Illuminate\Http\Request;
class JobController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(Job $job)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Job $job)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Job $job)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Job $job)
{
//
}
}

11
app/Models/Computer.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Computer extends Model
{
use HasFactory;
}

11
app/Models/Job.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Job extends Model
{
use HasFactory;
}

View File

@ -0,0 +1,34 @@
<?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::create('computers', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
$table->string('name', length: 256);
$table->string('cpu', length: 256)->nullable();
$table->string('motherboard', length: 256)->nullable();
$table->string('gpu', length: 256)->nullable();
$table->string('additional_info', length: 256)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('computers');
}
};

View File

@ -0,0 +1,31 @@
<?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::create('jobs', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('computer_id');
$table->foreign('computer_id')->references('id')->on('computers');
$table->string('description', length: 256);
$table->boolean('status')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
}
};

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M6,2C4.89,2 4,2.89 4,4V12C4,13.11 4.89,14 6,14H18C19.11,14 20,13.11 20,12V4C20,2.89 19.11,2 18,2H6M6,4H18V12H6V4M4,15C2.89,15 2,15.89 2,17V20C2,21.11 2.89,22 4,22H20C21.11,22 22,21.11 22,20V17C22,15.89 21.11,15 20,15H4M8,17H20V20H8V17M9,17.75V19.25H13V17.75H9M15,17.75V19.25H19V17.75H15Z" style="fill: rgb(42, 139, 42);"/>
</svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@ -31,9 +31,9 @@
:class="menuOpen ? 'mt-2 mb-2 pa-2' : 'mt-10 mb-3 pa-5'" :class="menuOpen ? 'mt-2 mb-2 pa-2' : 'mt-10 mb-3 pa-5'"
> >
<p class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><RouterLink to="/" class="nav-link text-decoration-none">Главная</RouterLink></p> <p class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><RouterLink to="/" class="nav-link text-decoration-none">Главная</RouterLink></p>
<p v-if="user == null" class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><RouterLink to="/login" class="nav-link text-decoration-none">Войти</RouterLink></p> <p v-if="!authenticated" class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><RouterLink to="/login" class="nav-link text-decoration-none">Войти</RouterLink></p>
<p v-if="user == null" class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><RouterLink to="/register" class="nav-link text-decoration-none">Регистрация</RouterLink></p> <p v-if="!authenticated" class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><RouterLink to="/register" class="nav-link text-decoration-none">Регистрация</RouterLink></p>
<p v-else class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><a href="#" class="nav-link text-decoration-none" @click="logout">Выйти</a></p> <p v-if="authenticated" class="ml-3 mr-3" :class="menuOpen ? 'text-body-1' : 'text-h6'"><a href="#" class="nav-link text-decoration-none" @click="logout">Выйти</a></p>
</v-sheet> </v-sheet>
<v-sheet class="rounded-lg main-bg h-100 mt-3 mr-10 ml-10 mb-10 pa-5"> <v-sheet class="rounded-lg main-bg h-100 mt-3 mr-10 ml-10 mb-10 pa-5">
<RouterView v-slot="{ Component }"> <RouterView v-slot="{ Component }">
@ -48,28 +48,23 @@
</template> </template>
<script> <script>
import {th} from "vuetify/locale"; import {useUserStore} from "./store/auth.js";
import {useAuthStore} from "./store/auth.js"; import {watch} from "vue";
export default { export default {
name: "App", name: "App",
data: () => ({ data: () => ({
userStore: useUserStore(),
windowHeight: document.documentElement.clientHeight, windowHeight: document.documentElement.clientHeight,
windowWidth: document.documentElement.clientWidth, windowWidth: document.documentElement.clientWidth,
isWide: window.innerWidth >= 460, isWide: window.innerWidth >= 460,
menuOpen: false menuOpen: false,
authenticated: false
}), }),
computed: {
user() {
const authStore = useAuthStore();
return authStore.user;
},
},
methods: { methods: {
logout() { logout() {
const authStore = useAuthStore(); this.userStore.logout();
authStore.logout();
this.username = 'guest';
this.$router.push('/login'); this.$router.push('/login');
}, },
resizeEventHandler(e) { resizeEventHandler(e) {
@ -90,6 +85,10 @@ export default {
mounted() { mounted() {
this.resizeEventHandler(); this.resizeEventHandler();
window.addEventListener("resize", this.resizeEventHandler, { passive: true }); window.addEventListener("resize", this.resizeEventHandler, { passive: true });
this.userStore.checkUser();
watch(this.userStore, (newStore)=>{
this.authenticated = newStore.user !== null && newStore.user !== undefined;
});
} }
} }
</script> </script>

View File

@ -1,31 +1,103 @@
import { defineStore } from 'pinia'; import {defineStore} from 'pinia'
import axios from 'axios'; import axios from "axios";
export const useAuthStore = defineStore('auth', { export const useUserStore = defineStore('user', {
state: () => ({ state: () => ({
user: null, user: null,
token: localStorage.getItem('token') || null, token: localStorage.getItem('auth_token') || null,
}), }),
actions: { actions: {
async login(credentials) { setUser(user) {
const response = await axios.post('/api/login', credentials); this.user = user;
this.user = response.data.user;
this.token = response.data.token;
localStorage.setItem('token', response.data.token);
}, },
async register(credentials) { setToken(token) {
const response = await axios.post('/api/register', credentials); this.token = token;
this.user = response.data.user; localStorage.setItem('auth_token', token);
this.token = response.data.token; },
localStorage.setItem('token', response.data.token); async checkUser() {
await axios.get(
'/api/auth/user',
{
headers:
{
Authorization: `Bearer ${this.token}`,
token: this.token
}
}
).then((res) => {
this.setUser(res.data);
return true;
}).catch((error) => {
this.nullifyUser();
return false;
})
},
async login(email, password, rememberMe) {
await axios.post(
'/api/auth/login',
{
'email': email,
'password': password,
'remember_me': rememberMe
}).then((res) => {
this.setUser(res.data.user);
this.setToken(res.data.accessToken);
return true;
}).catch((error) => {
if (!error.response) {
return false;
}
return error.response.data.message;
})
},
async registration(login, email, password, repeatPassword) {
await axios.post(
'/api/auth/register',
{
'name': login,
'email': email,
'password': password,
'c_password': repeatPassword
}).then((res) => {
this.setUser(res.data.user);
this.setToken(res.data.accessToken);
return true;
}).catch((error) => {
if (!error.response) {
return false;
}
return error.response.data.message;
})
}, },
logout() { logout() {
this.user = null; axios.get('/api/auth/logout',
this.token = null; {
localStorage.removeItem('token'); headers:
{
Authorization: `Bearer ${this.token}`,
token: this.token
}
}
);
this.nullifyUser();
}, },
nullifyUser() {
this.setUser(null);
this.setToken(null);
},
async getUsername(id){
let result = null;
await axios.get('/api/wish/username',
{
params:
{
user_id: id
}
}
).then((res) => {
result = res;
});
return result;
}
}, },
getters: { })
isAuthenticated: (state) => !!state.token,
},
});

View File

@ -1,31 +1,37 @@
<script> <script>
import { useAuthStore } from '../store/auth.js'; import { watch } from 'vue';
import { useUserStore } from '../store/auth.js';
export default { export default {
name: "About", name: "About",
data() { data() {
return { return {
userStore: useUserStore(),
authenticated: false,
username: 'guest' username: 'guest'
}; };
}, },
computed: {
user() {
const authStore = useAuthStore();
return authStore.user;
},
},
methods: { methods: {
logout() { logout() {
const authStore = useAuthStore(); this.userStore.logout();
authStore.logout();
this.username = 'guest';
this.$router.push('/login'); this.$router.push('/login');
}, },
setStored(stored){
this.userStored = stored;
}
},
mounted() {
this.userStore.checkUser();
watch(this.userStore, (newStore)=>{
this.authenticated = newStore.user !== null && newStore.user !== undefined;
});
} }
} }
</script> </script>
<template> <template>
<v-label class="text-h3">Welcome {{ user != null ? user.name : 'guest' }}!</v-label><br> <div class="d-flex justify-center">
<v-label class="text-h3 h-auto w-auto pa-2">Welcome {{ authenticated ? userStore.user['name'] : 'guest' }}!</v-label><br>
</div>
</template> </template>
<style scoped> <style scoped>

View File

@ -6,8 +6,11 @@
<v-text-field type="email" v-model="email" label="E-mail" class="flex-grow-0" required></v-text-field> <v-text-field type="email" v-model="email" label="E-mail" class="flex-grow-0" required></v-text-field>
<v-label>Пароль:</v-label> <v-label>Пароль:</v-label>
<v-text-field type="password" v-model="password" label="Пароль" class="flex-grow-0" required></v-text-field> <v-text-field type="password" v-model="password" label="Пароль" class="flex-grow-0" required></v-text-field>
<v-checkbox v-model="rememberMe" label="Запомнить меня"></v-checkbox>
<div class="d-flex justify-center" :class="isWide ? '' : 'flex-column align-center'"> <div class="d-flex justify-center" :class="isWide ? '' : 'flex-column align-center'">
<v-btn type="submit" color="#F0A068FF" class="ma-5 flex-grow-0" :class="isWide ? 'w-25' : 'w-100 text-body-1'" block>Войти</v-btn> <!--<div>-->
<v-btn type="submit" color="#F0A068FF" class="ma-5 flex-grow-0" :class="isWide ? 'w-25' : 'w-100 text-body-1'" :loading="loading">Войти</v-btn>
<!--</div>-->
<router-link to="/register" class="text-decoration-none link-no-color ma-5" :class="isWide ? 'w-25' : 'w-100'"> <router-link to="/register" class="text-decoration-none link-no-color ma-5" :class="isWide ? 'w-25' : 'w-100'">
<v-btn color="#F0A068FF" :class="isWide ? 'w-100' : 'w-100 text-body-1'">Регистрация</v-btn> <v-btn color="#F0A068FF" :class="isWide ? 'w-100' : 'w-100 text-body-1'">Регистрация</v-btn>
</router-link> </router-link>
@ -29,7 +32,7 @@
</template> </template>
<script> <script>
import { useAuthStore } from '../store/auth.js'; import { useUserStore } from '../store/auth.js';
export default { export default {
name: "Login", name: "Login",
props: { props: {
@ -37,16 +40,33 @@ export default {
}, },
data() { data() {
return { return {
userStore: useUserStore(),
email: '', email: '',
password: '', password: '',
rememberMe: false,
loading: false,
errorMessage: '',
errorMessageContainerStyle: 'display: none;'
}; };
}, },
methods: { methods: {
async login() { async login() {
const authStore = useAuthStore(); this.loading = true;
await authStore.login({ await this.userStore.login(this.email, this.password, this.rememberMe).then((isLogged)=>{
email: this.email, this.loading = false;
password: this.password, if (typeof isLogged == "boolean") {
if (isLogged){
this.errorMessage = '';
this.errorMessageContainerStyle = 'display: none;';
this.$router.push('/');
} else {
this.errorMessage = 'Authentication error';
this.errorMessageContainerStyle = '';
}
} else if (typeof isLogged == "string") {
this.errorMessage = isLogged;
this.errorMessageContainerStyle = '';
}
}); });
this.$router.push('/'); this.$router.push('/');
}, },

View File

@ -1,5 +1,5 @@
<script> <script>
import { useAuthStore } from '../store/auth.js'; import { useUserStore } from '../store/auth.js';
export default { export default {
name: "Register", name: "Register",
props: { props: {
@ -7,20 +7,39 @@ export default {
}, },
data() { data() {
return { return {
userStore: useUserStore(),
name: '', name: '',
email: '', email: '',
password: '', password: '',
c_password: '',
errorMessage: '',
errorMessageContainerStyle: 'display: none;',
showPassword: false,
showRepeatPassword: false,
loading: false
}; };
}, },
methods: { methods: {
async register() { async register() {
const authStore = useAuthStore(); this.loading = true;
await authStore.register({ await this.userStore.registration(this.name, this.email, this.password, this.c_password).then((isRegistred)=>{
name: this.name, this.loading = false;
email: this.email, if (typeof isRegistred == "boolean") {
password: this.password, if (isRegistred){
this.errorMessage = '';
this.errorMessageContainerStyle = 'display: none;';
this.$router.push('/');
} else {
this.errorMessage = 'Registration error';
this.errorMessageContainerStyle = '';
}
} else if (typeof isRegistred == "string") {
this.errorMessage = isRegistred;
this.errorMessageContainerStyle = '';
}
}); });
this.$router.push('/');
}, },
} }
} }
@ -36,8 +55,10 @@ export default {
<v-text-field type="email" v-model="email" label="E-mail" class="flex-grow-0" required></v-text-field> <v-text-field type="email" v-model="email" label="E-mail" class="flex-grow-0" required></v-text-field>
<v-label>Пароль:</v-label> <v-label>Пароль:</v-label>
<v-text-field type="password" v-model="password" label="Пароль" class="flex-grow-0" required></v-text-field> <v-text-field type="password" v-model="password" label="Пароль" class="flex-grow-0" required></v-text-field>
<v-text-field type="password" v-model="c_password" label="Повторите пароль" class="flex-grow-0" required></v-text-field>
<v-label :style="errorMessageContainerStyle">{{ errorMessage }}</v-label>
<div class="d-flex justify-center" :class="isWide ? '' : 'flex-column align-center'"> <div class="d-flex justify-center" :class="isWide ? '' : 'flex-column align-center'">
<v-btn type="submit" color="#F0A068FF" class="ma-5" :class="isWide ? 'w-25' : 'w-100 text-body-1'" block>Зарегистрироваться</v-btn> <v-btn type="submit" color="#F0A068FF" class="ma-5" :class="isWide ? 'w-25' : 'w-100 text-body-1'" :loading="loading">Зарегистрироваться</v-btn>
<router-link to="/login" class="text-decoration-none link-no-color ma-5" :class="isWide ? 'w-25' : 'w-100'"> <router-link to="/login" class="text-decoration-none link-no-color ma-5" :class="isWide ? 'w-25' : 'w-100'">
<v-btn color="#F0A068FF" :class="isWide ? 'w-100' : 'w-100 text-body-1'">Вход</v-btn> <v-btn color="#F0A068FF" :class="isWide ? 'w-100' : 'w-100 text-body-1'">Вход</v-btn>
</router-link> </router-link>

View File

@ -3,6 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="images/favicon.svg" sizes="any" type="image/svg+xml">
<title>Журнал ТО</title> <title>Журнал ТО</title>
@vite('resources/app.js') @vite('resources/app.js')
@vite('resources/css/app.css') @vite('resources/css/app.css')

View File

@ -1,6 +1,7 @@
<?php <?php
use App\Http\Controllers\API\AuthController; use App\Http\Controllers\API\AuthController;
use App\Http\Controllers\ComputerController;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@ -19,6 +20,19 @@ Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user(); return $request->user();
}); });
Route::post('/register', [AuthController::class, 'register']); Route::group(['prefix' => 'auth'], function () {
Route::post('/login', [AuthController::class, 'login']); Route::post('login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum'); Route::post('register', [AuthController::class, 'register']);
Route::group(['middleware' => 'auth:sanctum'], function() {
Route::get('logout', [AuthController::class, 'logout']);
Route::get('user', [AuthController::class, 'user']);
});
});
Route::group(['prefix' => 'data'], function () {
Route::get('computers/all', [ComputerController::class, 'index']);
Route::post('computers/create', [ComputerController::class, 'create']);
Route::post('computers/save', [ComputerController::class, 'update']);
Route::post('computers/delete', [ComputerController::class, 'destroy']);
});