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": {
|
||||
"@prisma/client": "^4.15.0",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/netmask": "^2.0.1",
|
||||
"body-parser": "^1.20.2",
|
||||
"bree": "^9.1.3",
|
||||
"concurrently": "^8.2.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.1.4",
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Request, Response, Router } from 'express'
|
||||
import { AuthenticationService } from '../services/AuthenticationService.js'
|
||||
import { InvalidCredentialsError } from 'ldapts'
|
||||
|
||||
const router = Router()
|
||||
|
||||
|
@ -8,7 +9,7 @@ class LoginController {
|
|||
const { username, password } = req.body
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -16,6 +17,10 @@ class LoginController {
|
|||
const token = await AuthenticationService.login(username, password)
|
||||
res.json({ token })
|
||||
} 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 })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import express, { Request, Response } from 'express'
|
||||
import bodyParser from 'body-parser'
|
||||
import cors from 'cors'
|
||||
|
||||
import { populateUserMiddleware } from './middlewares/populateUserMiddleware.js'
|
||||
import { authMiddleware } from './middlewares/authMiddleware.js'
|
||||
|
@ -12,6 +13,7 @@ import PrinterDiscoveryRouter from './controllers/PrinterDiscoveryController.js'
|
|||
|
||||
export const app = express()
|
||||
|
||||
app.use(cors())
|
||||
app.use(loggerMiddleware)
|
||||
|
||||
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";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/login",
|
||||
component: () => import("@/layouts/simple/Default.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "Login",
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "login" */ "@/views/Login.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
component: () => import("@/layouts/default/Default.vue"),
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
<template>
|
||||
<div><h1>Home</h1></div>
|
||||
<div>
|
||||
<h1>Home</h1>
|
||||
{{ printers }}
|
||||
</div>
|
||||
</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