feat(Auth): Compete auth-middleware

This commit is contained in:
漩葵 2024-07-06 18:31:26 +08:00
parent 07143e2048
commit d167cb7265
18 changed files with 495 additions and 69 deletions

11
composables/user.ts Normal file
View File

@ -0,0 +1,11 @@
export async function getUserId() {
const auth = useCookie("auth");
return await $fetch("/api/user/auth", {
method: "GET",
query: {
auth: auth.value,
},
}).then((res: number) => {
return res;
});
}

View File

@ -0,0 +1,6 @@
export default defineNuxtRouteMiddleware(async (to, from) => {
if ((await getUserId()) != 1) {
ElMessage("禁止访问");
return navigateTo("/", { replace: true });
}
});

View File

@ -4,24 +4,24 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
ElMessage("未登录或cookie未开启");
return navigateTo("/user/login", { replace: true });
} else {
const { data: result } = await useFetch("/api/user/auth", {
const result = await $fetch("/api/user/auth", {
method: "post",
body: {
auth: auth.value,
},
});
if (!result.value?.login && to.path !== "/user/test") {
if (result.value?.code == 0) {
if (!result.login && to.path !== "/user/test") {
if (result.code == 0) {
ElMessage("未登录");
} else if (result.value?.code == 2) {
} else if (result.code == 2) {
ElMessage("登录超时,请重新登录");
auth.value = undefined;
} else {
ElMessage(result.value?.code);
ElMessage("error" + result.code);
}
return navigateTo("/user/login");
} else {
console.log(auth.value);
//console.log(auth.value);
}
}
});

View File

@ -5,26 +5,26 @@
</Head>
<client-only>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="appid" label="AppID" width="180" />
<el-table-column prop="name" label="申请人" width="180" />
<el-table-column prop="phone" label="手机号" width="180" />
<el-table-column prop="id" label="AppID" width="180" />
<el-table-column prop="applicant" label="申请人" width="180" />
<el-table-column prop="area" label="地区" width="180" />
<el-table-column label="配置" width="300">
<template #default="scope">
<el-icon color="blue"><ElIcon-Cpu /></el-icon>
{{ scope.row.resource.cpu }} Core
{{ scope.row.cpu }} Core
<el-icon color="green"><ElIcon-Stopwatch /></el-icon
>{{ scope.row.resource.ram }} GB
>{{ scope.row.ram }} GB
<el-icon color="gray"><ElIcon-Memo /></el-icon>
{{ scope.row.resource.disk }} GB
{{ scope.row.disk }} GB
</template>
</el-table-column>
<el-table-column prop="usage" label="用途" width="360" />
<el-table-column prop="desc" label="用途" width="360" />
<el-table-column label="状态" width="300">
<template #default="scope">
<el-icon :color="scope.row.apply ? 'green' : 'orange'"
<el-icon :color="scope.row.deploy ? 'green' : 'orange'"
><ElIcon-Stamp
/></el-icon>
{{ scope.row.apply ? "通过" : "待审核" }}
{{ scope.row.deploy ? "通过" : "待审核" }}
</template>
</el-table-column>
<el-table-column label="操作">
@ -49,30 +49,7 @@
</client-only>
</template>
<script lang="ts" setup>
const tableData = [
{
appid: "1",
name: "漩葵",
phone: 1922949224,
usage: "用于开展服务",
resource: {
cpu: 1,
ram: 2,
disk: 10,
},
apply: false,
},
{
appid: "2",
name: "BrianLing",
phone: 1922949224,
usage: "用于开展服务",
resource: {
cpu: 1,
ram: 2,
disk: 10,
},
apply: true,
},
];
import type { Application } from "~/types/Application";
const data: Application[] = await $fetch("/api/admin/applications");
const tableData = ref(data);
</script>

View File

@ -12,6 +12,7 @@
>
<ClientOnly>
<Vueform
@success="AppSuccess"
endpoint="/api/test"
method="POST"
view="tabs"
@ -28,49 +29,67 @@
label="项目名称"
placeholder="项目名称"
:columns="{ container: 4, label: 4, wrapper: 12 }"
rules="required"
:rules="['required', isCreate]"
/>
<SelectElement
name="select"
default="辽宁一区"
name="area"
default="1"
label="地区"
:native="false"
:items="['辽宁一区', '辽宁二区', '江西一区']"
:items="{ 1: '辽宁一区', 2: '辽宁二区', 3: '江西一区' }"
:columns="{ container: 4, label: 3, wrapper: 12 }"
rules="required"
/>
<RadiogroupElement
default="2 Core"
default="2"
label="CPU核心数"
name="cpu"
:items="['2 Core', '4 Core', '6 Core', 'More']"
:items="{ 2: '2 Core', 4: '4 Core', 6: '6 Core', 10: 'More' }"
view="tabs"
/>
<RadiogroupElement
default="2 GB"
default="2"
label="RAM容量"
name="ram"
:items="['2 GB', '4 GB', '6 GB', 'More']"
:items="{ 2: '2 GB', 4: '4 GB', 6: '6 GB', 10: 'More' }"
view="tabs"
/>
<SliderElement
sync
name="disk"
label="磁盘容量"
:default="5"
:min="1"
:max="40"
:format="(v: number) => v > 1 ? `${Math.round(v)} GB` : '1 GB'"
:columns="{ container: 12, label: 12, wrapper: 12 }"
:add-classes="{
ElementLayout: {
innerWrapper: 'mt-12',
},
}"
/>
<EditorElement name="usage" label="用途说明" rules="required|max:500" />
<EditorElement
name="desc"
label="用途说明"
rules="required|max:500"
:hide-tools="['attach']"
/>
<StaticElement
label="人机验证"
name="static"
:columns="{ container: 12, label: 12, wrapper: 12 }"
>
<DefaultSilderVerify
@success="sliderHandleSuccess"
@failed="sliderHandleError"
/>
</StaticElement>
<HiddenElement :default="uid" name="uid" />
<HiddenElement :default="auth" name="auth" />
<ButtonElement
:disabled="isBot"
name="submit"
button-label="提交申请"
align="center"
@ -82,9 +101,43 @@
</div>
</template>
<script setup lang="ts">
import PhoneElemen from "@vueform/vueform";
import { Validator } from "@vueform/vueform";
definePageMeta({
middleware: ["auth"],
});
const uid: number = await getUserId();
const isBot = ref(true);
const auth = useCookie("auth");
function sliderHandleSuccess() {
ElMessage({ message: "人机验证已通过", type: "success" });
isBot.value = false;
}
function sliderHandleError() {
ElMessage({ message: "人机验证未通过", type: "warning" });
}
const isCreate = class extends Validator {
get msg() {
return "项目名已存在";
}
check(value: string) {
if (value == "") {
return 0;
}
return $fetch("/api/application", {
method: "POST",
body: { name: value },
}).then((res) => {
return res.name;
});
}
};
const form = ref();
function AppSuccess(response: { data: { code: number; msg: string } }) {
if (response.data.code) {
ElMessage({ message: response.data.msg, type: "success" });
navigateTo("/");
} else {
ElMessage({ message: response.data.msg, type: "warning" });
}
}
</script>

View File

@ -4,10 +4,12 @@
<Title>FreePotato Server</Title>
<Meta name="description" content="免费服务器~" />
</Head>
<ElRow :gutter="10" align="middle" style="height: 900px">
<ElCol :span="24"><IndexNewsCarouel /></ElCol>
<ElCol :span="24"><IndexNewsStatus /></ElCol>
</ElRow>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
const a = await getUserId();
console.info(a);
</script>

View File

@ -1,11 +1,63 @@
<template>
<Head>
<Title>用户信息</Title>
<Title>个人界面</Title>
<Meta name="description" />
</Head>
<client-only>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="AppID" width="180" />
<el-table-column prop="applicant" label="申请人" width="180" />
<el-table-column prop="area" label="地区" width="180" />
<el-table-column label="配置" width="300">
<template #default="scope">
<el-icon color="blue"><ElIcon-Cpu /></el-icon>
{{ scope.row.cpu }} Core
<el-icon color="green"><ElIcon-Stopwatch /></el-icon
>{{ scope.row.ram }} GB
<el-icon color="gray"><ElIcon-Memo /></el-icon>
{{ scope.row.disk }} GB
</template>
</el-table-column>
<el-table-column prop="desc" label="用途" width="360" />
<el-table-column label="状态" width="300">
<template #default="scope">
<el-icon :color="scope.row.deploy ? 'green' : 'orange'"
><ElIcon-Stamp
/></el-icon>
{{ scope.row.deploy ? "通过" : "待审核" }}
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button
size="small"
type="success"
@click="ElMessage({ message: '查看', type: 'success' })"
>
查看
</el-button>
<el-button
size="small"
type="danger"
@click="ElMessage({ message: '取消', type: 'warning' })"
>
取消
</el-button>
</template>
</el-table-column>
</el-table>
</client-only>
</template>
<script setup lang="ts">
<script lang="ts" setup>
import type { Application } from "~/types/Application";
definePageMeta({
middleware: ["auth"],
});
const data: Application[] = await $fetch("/api/user/application", {
method: "POST",
body: {
uid: await getUserId(),
},
});
const tableData = ref(data);
</script>

113
prisma/test.prisma Normal file
View File

@ -0,0 +1,113 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-3.0.x"]
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
phone String @unique
username String?
password String
applications Application[]
loginlogs Loginlogs[]
Vm Vm[]
}
model Adminer {
id Int @id @default(autoincrement())
adminId Int
}
model Application {
id Int @id @default(autoincrement())
name String
area String
cpu Int
ram Int
disk Int
desc String
deploy Boolean @default(false)
applicant User @relation(fields: [applicantId], references: [id])
applicantId Int
}
model Register {
id Int @id @default(autoincrement())
phone String
deadline DateTime
code String
}
model Loginlogs {
id Int @id @default(autoincrement())
outtime DateTime
ip String
loginer User @relation(fields: [userid], references: [id])
userid Int
token String @unique
}
//Config
model Web {
ConfigId Int @id
ConfigName String
ConfigValue String
}
model Cluster {
ClusterId Int @id
Name String
Ip String
Username String
Password String
Gateway String
Resource String
Status String
Nodes Node[]
}
model Node {
NodeId Int @id
Cluster Cluster @relation(fields: [ClusterId], references: [ClusterId])
ClusterId Int
Resource String
Status String
Vms Vm[]
}
model Template {
TemplateId Int @id @default(autoincrement())
OS String
Type String
Path String
Cpu String
Ram String
Disk String
Ports String
Vm Vm[]
}
//datasource
model Ip {
Adress String @id
Vmid Int
Vm Vm @relation(fields: [Vmid], references: [Vmid])
}
model Port {
Port Int @id
Vmid Int
Vm Vm @relation(fields: [Vmid], references: [Vmid])
}
model Vm {
Vmid Int @id @default(autoincrement())
NodeId Int
Node Node @relation(fields: [NodeId], references: [NodeId])
TemplateId Int
Template Template @relation(fields: [TemplateId], references: [TemplateId])
UserId Int
User User @relation(fields: [UserId], references: [id])
Ip Ip[]
SshPort Int
Ports Port[]
}

View File

@ -0,0 +1,51 @@
import { PrismaClient } from "@prisma/client";
import type { Application } from "~/types/Application";
const db = new PrismaClient();
type app = {
applicant: {
username: string | null;
};
} & {
id: number;
name: string;
area: string;
cpu: number;
ram: number;
disk: number;
desc: string;
deploy: boolean;
applicantId: number;
};
function formatApplication(raw: app[]): Application[] {
var logs: Application[] = [];
raw.forEach((element: app) => {
logs.push({
id: element.id,
area: element.area,
name: element.name,
applicant: element.applicant.username || "none",
cpu: element.cpu,
ram: element.ram,
disk: element.disk,
desc: element.desc,
deploy: element.deploy,
});
});
return logs;
}
export default defineEventHandler(async (event) => {
const applications = await db.application.findMany({
orderBy: {
id: "desc", // 'asc' 表示升序,'desc' 表示降序
},
include: {
applicant: {
select: {
username: true,
},
},
},
});
await db.$disconnect();
return formatApplication(applications);
});

View File

@ -0,0 +1,6 @@
import { PrismaClient } from "@prisma/client";
const db = new PrismaClient();
export default defineEventHandler(async (event) => {
const body = await readBody(event);
return body;
});

View File

@ -0,0 +1,15 @@
import { PrismaClient } from "@prisma/client";
const db = new PrismaClient();
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const res = {
name:
(await db.application.findFirst({
where: {
name: body.name,
},
})) == null,
};
await db.$disconnect();
return res;
});

View File

@ -1,7 +1,66 @@
import { PrismaClient } from "@prisma/client";
const db = new PrismaClient();
export default defineEventHandler(async (event) => {
const body: {
name: string;
area: string;
cpu: string;
ram: string;
disk: string;
desc: string;
uid: string;
auth: string;
} = await readBody(event);
const isAuth: { login: boolean; code: number } = await $fetch(
"/api/user/auth",
{
method: "POST",
body: {
auth: body.auth,
},
}
);
if (isAuth.login) {
await db.application.create({
data: {
name: body.name,
area: body.area,
cpu: parseInt(body.cpu),
ram: parseInt(body.ram),
disk: parseInt(body.disk),
desc: body.desc,
applicantId: parseInt(body.uid),
},
});
export default defineEventHandler((event) => {
return event;
await db.$disconnect();
return {
code: 1,
msg: "申请提交成功",
};
} else {
if (isAuth.code == 0) {
console.error(isAuth);
console.error(JSON.stringify(body));
return {
code: 0,
msg: "未登录",
};
} else {
return {
code: 0,
msg: "登陆超时",
};
}
}
return body;
});
/* { name: '1231',
area: '1',
cpu: '2',
ram: '2',
disk: '5',
desc: '<div>12313</div>',
uid: '1',
auth: 'a19705902b2ba12fbe52930b34802ab1' } */

View File

@ -0,0 +1,55 @@
import { PrismaClient } from "@prisma/client";
import type { Application } from "~/types/Application";
const db = new PrismaClient();
type app = {
applicant: {
username: string | null;
};
} & {
id: number;
name: string;
area: string;
cpu: number;
ram: number;
disk: number;
desc: string;
deploy: boolean;
applicantId: number;
};
function formatApplication(raw: app[]): Application[] {
var logs: Application[] = [];
raw.forEach((element: app) => {
logs.push({
id: element.id,
area: element.area,
name: element.name,
applicant: element.applicant.username || "none",
cpu: element.cpu,
ram: element.ram,
disk: element.disk,
desc: element.desc,
deploy: element.deploy,
});
});
return logs;
}
export default defineEventHandler(async (event) => {
const body = await readBody(event);
const applications = await db.application.findMany({
where: {
applicantId: body.uid,
},
orderBy: {
id: "desc", // 'asc' 表示升序,'desc' 表示降序
},
include: {
applicant: {
select: {
username: true,
},
},
},
});
await db.$disconnect();
return formatApplication(applications);
});

View File

@ -0,0 +1,23 @@
import { PrismaClient } from "@prisma/client";
const db = new PrismaClient();
export default defineEventHandler(async (event) => {
const query: { auth: string } = getQuery(event);
let userid: number = 0;
userid = await db.loginlogs
.findFirst({
where: { token: query.auth },
select: {
userid: true,
},
})
.then((res) => {
if (res != null) {
return res.userid;
} else {
return 0;
}
});
//console.info(userid);
return userid;
});

View File

@ -4,7 +4,7 @@ const db = new PrismaClient();
async function auth(auth: string) {
const res = await db.loginlogs.findFirst({
where: { token: auth.toString() },
where: { token: auth },
});
//return JSON.stringify((await res).values)
if (res == null) {

View File

@ -1,9 +1,11 @@
export type Application = {
name: string
area: string
cpu: number
ram: number
disk: number
usage: string
applicantId: number
}
id: number;
name: string;
area: string;
cpu: number;
ram: number;
disk: number;
desc: string;
applicant: string;
deploy: boolean;
};

1
types/Application/index.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export { Application } from "./Application.ts";