Basic login
This commit is contained in:
parent
3a9f46b85e
commit
2ead863ace
1120
package-lock.json
generated
1120
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -37,10 +37,12 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^4.15.0",
|
"@prisma/client": "^4.15.0",
|
||||||
|
"@types/cors": "^2.8.13",
|
||||||
"@types/netmask": "^2.0.1",
|
"@types/netmask": "^2.0.1",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"bree": "^9.1.3",
|
"bree": "^9.1.3",
|
||||||
"concurrently": "^8.2.0",
|
"concurrently": "^8.2.0",
|
||||||
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.1.4",
|
"dotenv": "^16.1.4",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Request, Response, Router } from 'express'
|
import { Request, Response, Router } from 'express'
|
||||||
import { AuthenticationService } from '../services/AuthenticationService.js'
|
import { AuthenticationService } from '../services/AuthenticationService.js'
|
||||||
|
import { InvalidCredentialsError } from 'ldapts'
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ class LoginController {
|
||||||
const { username, password } = req.body
|
const { username, password } = req.body
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
res.status(400).json({ error: 'Missing username or password' })
|
res.status(400).json({ error: 'Usuário e senha devem ser informados!' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +17,10 @@ class LoginController {
|
||||||
const token = await AuthenticationService.login(username, password)
|
const token = await AuthenticationService.login(username, password)
|
||||||
res.json({ token })
|
res.json({ token })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
if (error instanceof InvalidCredentialsError) {
|
||||||
|
res.status(401).json({ error: 'Usuário ou senha inválidos' })
|
||||||
|
return
|
||||||
|
}
|
||||||
res.status(401).json({ error: error.message })
|
res.status(401).json({ error: error.message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import express, { Request, Response } from 'express'
|
import express, { Request, Response } from 'express'
|
||||||
import bodyParser from 'body-parser'
|
import bodyParser from 'body-parser'
|
||||||
|
import cors from 'cors'
|
||||||
|
|
||||||
import { populateUserMiddleware } from './middlewares/populateUserMiddleware.js'
|
import { populateUserMiddleware } from './middlewares/populateUserMiddleware.js'
|
||||||
import { authMiddleware } from './middlewares/authMiddleware.js'
|
import { authMiddleware } from './middlewares/authMiddleware.js'
|
||||||
|
@ -12,6 +13,7 @@ import PrinterDiscoveryRouter from './controllers/PrinterDiscoveryController.js'
|
||||||
|
|
||||||
export const app = express()
|
export const app = express()
|
||||||
|
|
||||||
|
app.use(cors())
|
||||||
app.use(loggerMiddleware)
|
app.use(loggerMiddleware)
|
||||||
|
|
||||||
app.use('/api', bodyParser.json())
|
app.use('/api', bodyParser.json())
|
||||||
|
|
1
web/.env.dev
Normal file
1
web/.env.dev
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
0
web/.env.prod
Normal file
0
web/.env.prod
Normal file
27
web/src/api.ts
Normal file
27
web/src/api.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
const BASE_URL = process.env.BASE_URL || "http://localhost:8000/api/";
|
||||||
|
|
||||||
|
export async function api(endpoint: string, options: any) {
|
||||||
|
const token = localStorage.getItem("token");
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
options.headers = {
|
||||||
|
...options.headers,
|
||||||
|
Authorization: token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
options.headers = {
|
||||||
|
...options.headers,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(BASE_URL + endpoint, options);
|
||||||
|
|
||||||
|
const json = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(json.error);
|
||||||
|
}
|
13
web/src/auth.ts
Normal file
13
web/src/auth.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
const LOCAL_STORAGE_KEY = "token";
|
||||||
|
|
||||||
|
export function saveJwtToken(token: string) {
|
||||||
|
localStorage.setItem(LOCAL_STORAGE_KEY, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getJwtToken() {
|
||||||
|
return localStorage.getItem(LOCAL_STORAGE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeJwtToken() {
|
||||||
|
localStorage.removeItem(LOCAL_STORAGE_KEY);
|
||||||
|
}
|
9
web/src/layouts/simple/Default.vue
Normal file
9
web/src/layouts/simple/Default.vue
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<template>
|
||||||
|
<v-app>
|
||||||
|
<default-view />
|
||||||
|
</v-app>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import DefaultView from './View.vue'
|
||||||
|
</script>
|
11
web/src/layouts/simple/View.vue
Normal file
11
web/src/layouts/simple/View.vue
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<template>
|
||||||
|
<v-app id="inspire">
|
||||||
|
<v-main> <router-view /> </v-main>
|
||||||
|
</v-app>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const drawer = ref(true);
|
||||||
|
</script>
|
|
@ -2,6 +2,18 @@
|
||||||
import { createRouter, createWebHistory } from "vue-router";
|
import { createRouter, createWebHistory } from "vue-router";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
{
|
||||||
|
path: "/login",
|
||||||
|
component: () => import("@/layouts/simple/Default.vue"),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "Login",
|
||||||
|
component: () =>
|
||||||
|
import(/* webpackChunkName: "login" */ "@/views/Login.vue"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
component: () => import("@/layouts/default/Default.vue"),
|
component: () => import("@/layouts/default/Default.vue"),
|
||||||
|
|
|
@ -1,5 +1,23 @@
|
||||||
<template>
|
<template>
|
||||||
<div><h1>Home</h1></div>
|
<div>
|
||||||
|
<h1>Home</h1>
|
||||||
|
{{ printers }}
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup></script>
|
<script lang="ts" setup>
|
||||||
|
import { api } from "@/api";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const printers = ref([]);
|
||||||
|
|
||||||
|
async function getPrinters() {
|
||||||
|
const data = await api("printer", {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
|
||||||
|
printers.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrinters();
|
||||||
|
</script>
|
||||||
|
|
102
web/src/views/Login.vue
Normal file
102
web/src/views/Login.vue
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<template>
|
||||||
|
<v-container class="fill-height">
|
||||||
|
<v-form
|
||||||
|
:disabled="loading"
|
||||||
|
style="width: 480px"
|
||||||
|
class="mx-auto"
|
||||||
|
@submit.prevent="login"
|
||||||
|
v-model="valid"
|
||||||
|
>
|
||||||
|
<v-card :loading="loading">
|
||||||
|
<v-card-title class="mb-2 font-weight-regular"> Login </v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-text-field
|
||||||
|
placeholder="SIAPE"
|
||||||
|
variant="outlined"
|
||||||
|
label="Usuário"
|
||||||
|
color="primary"
|
||||||
|
v-model="username"
|
||||||
|
autocomplete="username"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<v-text-field
|
||||||
|
placeholder="Senha do SUAP"
|
||||||
|
type="password"
|
||||||
|
variant="outlined"
|
||||||
|
label="Senha"
|
||||||
|
color="primary"
|
||||||
|
v-model="password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
size="large"
|
||||||
|
:disabled="loading || !valid"
|
||||||
|
>
|
||||||
|
Entrar
|
||||||
|
</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
<div v-if="errors.length">
|
||||||
|
<v-alert
|
||||||
|
v-for="error in errors"
|
||||||
|
icon="mdi-alert-circle"
|
||||||
|
:key="error"
|
||||||
|
color="error"
|
||||||
|
class="mt-2"
|
||||||
|
variant="outlined"
|
||||||
|
closable
|
||||||
|
>
|
||||||
|
{{ error }}
|
||||||
|
</v-alert>
|
||||||
|
</div>
|
||||||
|
</v-form>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive } from "vue";
|
||||||
|
import { api } from "@/api";
|
||||||
|
import { saveJwtToken } from "@/auth";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
const username = ref<string>("");
|
||||||
|
const password = ref<string>("");
|
||||||
|
|
||||||
|
const valid = ref(false);
|
||||||
|
const errors = reactive<string[]>([]);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
errors.splice(0, errors.length);
|
||||||
|
|
||||||
|
try {
|
||||||
|
loading.value = true;
|
||||||
|
const data = await api("login", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({
|
||||||
|
username: username.value,
|
||||||
|
password: password.value,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = data.token;
|
||||||
|
|
||||||
|
saveJwtToken(token);
|
||||||
|
|
||||||
|
router.push({ name: "Home" });
|
||||||
|
} catch (error: any) {
|
||||||
|
errors.push(error.message);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user