diff --git a/assets/js/api/user.js b/assets/js/api/user.js new file mode 100644 index 0000000..d51969d --- /dev/null +++ b/assets/js/api/user.js @@ -0,0 +1,13 @@ +import axios from 'axios'; + +export default { + login: function (email, password) { + return axios.post('/login_api', { + email: email, + password: password + }); + }, + getUserData: function (userIri) { + return axios.get(userIri); + }, +} \ No newline at end of file diff --git a/assets/js/components/loginform.vue b/assets/js/components/loginform.vue new file mode 100644 index 0000000..c35d816 --- /dev/null +++ b/assets/js/components/loginform.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/assets/js/store/index.js b/assets/js/store/index.js index 4139ad8..ad71bc8 100644 --- a/assets/js/store/index.js +++ b/assets/js/store/index.js @@ -3,6 +3,7 @@ import Vuex from 'vuex' import booksmodule from './modules/books'; import bookprogressmodule from './modules/bookprogress'; import filemodule from './modules/filemodule'; +import usermodule from './modules/usermodule'; Vue.use(Vuex) @@ -10,7 +11,8 @@ export default new Vuex.Store({ modules:{ booksmodule, bookprogressmodule, - filemodule + filemodule, + usermodule } }) diff --git a/assets/js/store/modules/usermodule.js b/assets/js/store/modules/usermodule.js new file mode 100644 index 0000000..d387647 --- /dev/null +++ b/assets/js/store/modules/usermodule.js @@ -0,0 +1,81 @@ +import UserApi from "../../api/user"; +import { + LOGIN_START, + LOGIN_SUCCESS, + LOGIN_ERROR, + STORE_USER_INFO, +} from '../mutation-types.js' + +export default { + namespaced: true, + state: { + isLoading: false, + error: null, + user: null, + userUri: null, + }, + getters: { + isLoading(state) { + return state.isLoading; + }, + hasError(state) { + return state.error !== null; + }, + error(state) { + return state.error; + } + }, + mutations: { + [LOGIN_START](state) { + state.isLoading = true; + state.error = null; + state.user = null; + }, + [LOGIN_SUCCESS](state, userUri) { + state.isLoading = false; + state.error = null; + state.userUri = userUri; + }, + [STORE_USER_INFO](state, user) { + state.isLoading = false; + state.error = null; + state.user = user; + }, + [LOGIN_ERROR](state, error) { + state.isLoading = false; + state.error = error; + state.user = null; + }, + }, + actions: { + async login({dispatch, commit}, data) { + console.info(data.email); + console.info(data.password); + commit(LOGIN_START); + let response = await UserApi.login(data.email, data.password) + .then(response => { + console.log(response.data); + console.log(response.headers.location); + // commit(LOGIN_SUCCESS,response.data); + dispatch('getUserInfo', response.headers.location) + commit(LOGIN_SUCCESS, response.headers.location); + //this.$emit('user-authenticated', userUri); + //this.email = ''; + //this.password = ''; + }).catch(error => { + if (error.response.data.error) { + + commit(LOGIN_ERROR, error.response.data.error); + console.log(error.response.data.error); + } + + }).finally(() => { + // this.isLoading = false; + }) + }, + async getUserInfo({commit}, userUri) { + let userInfo = await UserApi.getUserData(userUri); + commit(STORE_USER_INFO, userInfo.data); + } + } +}; \ No newline at end of file diff --git a/assets/js/store/mutation-types.js b/assets/js/store/mutation-types.js index 0316286..15b7949 100644 --- a/assets/js/store/mutation-types.js +++ b/assets/js/store/mutation-types.js @@ -8,5 +8,10 @@ export const FETCHING_FILES = "FETCHING_FILES", FETCHING_FILES_SUCCESS = "FETCHING_FILES_SUCCESS", FETCHING_FILES_ERROR = "FETCHING_FILES_ERROR", - REMOVE_FILE_FROM_LIST = "REMOVE_FILE_FROM_LIST" + REMOVE_FILE_FROM_LIST = "REMOVE_FILE_FROM_LIST", + + LOGIN_START = "LOGIN_START", + LOGIN_SUCCESS = "LOGIN_SUCCESS", + LOGIN_ERROR = "LOGIN_ERROR", + STORE_USER_INFO = "STORE_USER_INFO" ; \ No newline at end of file diff --git a/assets/js/user.js b/assets/js/user.js new file mode 100644 index 0000000..e509f50 --- /dev/null +++ b/assets/js/user.js @@ -0,0 +1,6 @@ +import Vue from 'vue'; +import Loginform from "./components/loginform"; +import store from "./store/index"; + +Vue.component('Loginform', Loginform); +new Vue({store}).$mount('#app'); \ No newline at end of file diff --git a/config/packages/security.yaml b/config/packages/security.yaml index a1dec78..8c256c7 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -14,6 +14,10 @@ security: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: + json_login: + check_path: app_login_api + username_path: email + password_path: password lazy: true provider: app_user_provider custom_authenticator: App\Security\CustomAuthenticator diff --git a/src/Controller/IndexController.php b/src/Controller/IndexController.php new file mode 100644 index 0000000..f0b3d4e --- /dev/null +++ b/src/Controller/IndexController.php @@ -0,0 +1,18 @@ +render('index/index.html.twig', [ + 'controller_name' => 'IndexController', + ]); + } +} diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 15b59ce..e9c3724 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -2,19 +2,22 @@ namespace App\Controller; +use ApiPlatform\Core\Api\IriConverterInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; +use Symfony\Component\Validator\Constraints\Json; class SecurityController extends AbstractController { #[Route(path: '/login', name: 'app_login')] public function login(AuthenticationUtils $authenticationUtils): Response { - // if ($this->getUser()) { - // return $this->redirectToRoute('target_path'); - // } + if ($this->getUser()) { + return $this->redirectToRoute('app_book_index'); + } // get the login error if there is one $error = $authenticationUtils->getLastAuthenticationError(); @@ -24,9 +27,23 @@ class SecurityController extends AbstractController return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); } + #[Route(path: '/login_api', name: 'app_login_api', methods: ['POST'])] + public function login_api(IriConverterInterface $iriConverter): Response + { + return new Response(null, 204, [ + 'Location' => $iriConverter->getIriFromItem($this->getUser()) + ]); +// return $this->json([ +// 'user' => $this->getUser() ? $this->getUser()->getId() : null +// ]); + } + + #[Route(path: '/logout', name: 'app_logout')] public function logout(): void { - throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.'); + throw new \LogicException( + 'This method can be blank - it will be intercepted by the logout key on your firewall.' + ); } } diff --git a/src/Entity/User.php b/src/Entity/User.php index 9c729f9..47e3b49 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -2,11 +2,13 @@ namespace App\Entity; +use ApiPlatform\Core\Annotation\ApiResource; use App\Repository\UserRepository; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\UserInterface; +#[ApiResource] #[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Table(name: '`user`')] class User implements UserInterface, PasswordAuthenticatedUserInterface diff --git a/src/Security/CustomAuthenticator.php b/src/Security/CustomAuthenticator.php index 9e8d080..1f6d42f 100644 --- a/src/Security/CustomAuthenticator.php +++ b/src/Security/CustomAuthenticator.php @@ -49,9 +49,7 @@ class CustomAuthenticator extends AbstractLoginFormAuthenticator return new RedirectResponse($targetPath); } - // For example: - // return new RedirectResponse($this->urlGenerator->generate('some_route')); - throw new \Exception('TODO: provide a valid redirect inside '.__FILE__); + return new RedirectResponse($this->urlGenerator->generate('app_book_index')); } protected function getLoginUrl(Request $request): string diff --git a/templates/index/index.html.twig b/templates/index/index.html.twig new file mode 100644 index 0000000..8f5fa1c --- /dev/null +++ b/templates/index/index.html.twig @@ -0,0 +1,27 @@ +{% extends 'base.html.twig' %} + +{% block title %}Hello IndexController!{% endblock %} + +{% block body %} + + +
+ +
+{% endblock %} + +{% block javascripts %} + {{ encore_entry_script_tags('user') }} +{% endblock %} diff --git a/webpack.config.js b/webpack.config.js index 068abb3..546b896 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -23,6 +23,7 @@ Encore .addEntry('app', './assets/js/app.js') .addEntry('files', './assets/js/files.js') .addEntry('book', './assets/js/book.js') + .addEntry('user', './assets/js/user.js') // enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js) .enableStimulusBridge('./assets/controllers.json')