feat(/api/user): Compete login-code and auth-middleware
This commit is contained in:
parent
1abe25849b
commit
ba813ffcc8
@ -24,11 +24,12 @@
|
|||||||
<el-menu-item index="/area/jx2/game">游戏服务器</el-menu-item>
|
<el-menu-item index="/area/jx2/game">游戏服务器</el-menu-item>
|
||||||
</el-sub-menu> -->
|
</el-sub-menu> -->
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
<el-menu-item index="/forum" disabled>论坛</el-menu-item>
|
<el-menu-item index="forum" disabled>论坛</el-menu-item>
|
||||||
<el-menu-item index="/host" disabled>托管</el-menu-item>
|
<el-menu-item index="host" disabled>托管</el-menu-item>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<el-menu-item index="/login">登录</el-menu-item>
|
<el-menu-item v-if="auth" index="/user/login">登录</el-menu-item>
|
||||||
<el-menu-item index="register">注册</el-menu-item>
|
<el-menu-item v-if="auth" index="/user/register">注册</el-menu-item>
|
||||||
|
<el-menu-item v-if="!auth" @click="logoutNow">注销</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</client-only>
|
</client-only>
|
||||||
</template>
|
</template>
|
||||||
@ -36,9 +37,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const activeIndex = ref("1");
|
const activeIndex = ref("1");
|
||||||
const handleSelect = (key: string, keyPath: string[]) => {
|
const handleSelect = (key: string, keyPath: string[]) => {
|
||||||
console.log(key, keyPath);
|
|
||||||
navigateTo(key);
|
navigateTo(key);
|
||||||
};
|
};
|
||||||
|
const auth = ref(useCookie("auth").value == undefined);
|
||||||
|
function logoutNow() {
|
||||||
|
logout();
|
||||||
|
auth.value = true;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.flex-grow {
|
.flex-grow {
|
||||||
|
14
composables/logout.ts
Normal file
14
composables/logout.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 它将作为 useFoo() 可用(文件名的驼峰形式,不包括扩展名)
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const auth = useCookie("auth");
|
||||||
|
$fetch("/api/user/logout", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
auth: auth.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
auth.value = undefined;
|
||||||
|
navigateTo("/");
|
||||||
|
return 1;
|
||||||
|
}
|
0
middleware/admin.ts
Normal file
0
middleware/admin.ts
Normal file
27
middleware/auth.ts
Normal file
27
middleware/auth.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
|
const auth = useCookie("auth");
|
||||||
|
if (auth.value === undefined) {
|
||||||
|
ElMessage("未登录或cookie未开启");
|
||||||
|
return navigateTo("/user/login", { replace: true });
|
||||||
|
} else {
|
||||||
|
const { data: result } = await useFetch("/api/user/auth", {
|
||||||
|
method: "post",
|
||||||
|
body: {
|
||||||
|
auth: auth.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!result.value?.login && to.path !== "/user/test") {
|
||||||
|
if (result.value?.code == 0) {
|
||||||
|
ElMessage("未登录");
|
||||||
|
} else if (result.value?.code == 2) {
|
||||||
|
ElMessage("登录超时,请重新登录");
|
||||||
|
auth.value = undefined;
|
||||||
|
} else {
|
||||||
|
ElMessage(result.value?.code);
|
||||||
|
}
|
||||||
|
return navigateTo("/user/login");
|
||||||
|
} else {
|
||||||
|
console.log(auth.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
7
middleware/unauth.ts
Normal file
7
middleware/unauth.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||||
|
const auth = useCookie("auth");
|
||||||
|
if (auth.value === undefined) {
|
||||||
|
} else {
|
||||||
|
return navigateTo("/");
|
||||||
|
}
|
||||||
|
});
|
23
pages/admin/loginlogs/index.vue
Normal file
23
pages/admin/loginlogs/index.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<Head>
|
||||||
|
<Title>登录日志</Title>
|
||||||
|
<Meta name="description" />
|
||||||
|
</Head>
|
||||||
|
<client-only>
|
||||||
|
<el-table
|
||||||
|
:default-sort="{ prop: 'id', order: 'descending' }"
|
||||||
|
:data="tableData"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column prop="id" label="id" width="50" />
|
||||||
|
<el-table-column prop="username" label="用户" width="180" />
|
||||||
|
<el-table-column prop="date" label="登录时间" width="180" />
|
||||||
|
<el-table-column prop="ip" label="登录ip" width="180" />
|
||||||
|
</el-table>
|
||||||
|
</client-only>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { LoginLog } from "~/types/Log";
|
||||||
|
const data: LoginLog[] = await $fetch("/api/admin/loginlogs");
|
||||||
|
const tableData = ref(data);
|
||||||
|
</script>
|
@ -17,42 +17,6 @@
|
|||||||
view="tabs"
|
view="tabs"
|
||||||
style="padding: 30px"
|
style="padding: 30px"
|
||||||
>
|
>
|
||||||
<StaticElement
|
|
||||||
tag="h4"
|
|
||||||
align="center"
|
|
||||||
content="个人信息"
|
|
||||||
name="static"
|
|
||||||
/>
|
|
||||||
<TextElement
|
|
||||||
name="username"
|
|
||||||
label="昵称"
|
|
||||||
placeholder="昵称"
|
|
||||||
:columns="{ container: 4, label: 3, wrapper: 12 }"
|
|
||||||
rules="required|min:3"
|
|
||||||
/>
|
|
||||||
<PhoneElement
|
|
||||||
name="phone"
|
|
||||||
allow-incomplete
|
|
||||||
unmask
|
|
||||||
default="+86"
|
|
||||||
:include="['cn']"
|
|
||||||
label="手机号"
|
|
||||||
placeholder="+86"
|
|
||||||
:columns="{ container: 12, label: 1, wrapper: 4 }"
|
|
||||||
rules="required"
|
|
||||||
/>
|
|
||||||
<TextElement
|
|
||||||
name="code"
|
|
||||||
label="验证码"
|
|
||||||
placeholder="xxxxxx"
|
|
||||||
:columns="{ container: 4, label: 4, wrapper: 12 }"
|
|
||||||
rules="required"
|
|
||||||
/>
|
|
||||||
<ButtonElement
|
|
||||||
name="button"
|
|
||||||
:columns="{ container: 8, label: 3, wrapper: 12 }"
|
|
||||||
button-label="发送验证码"
|
|
||||||
/>
|
|
||||||
<StaticElement
|
<StaticElement
|
||||||
tag="h4"
|
tag="h4"
|
||||||
align="center"
|
align="center"
|
||||||
@ -119,5 +83,8 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import PhoneElemen from "@vueform/vueform";
|
import PhoneElemen from "@vueform/vueform";
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ["auth"],
|
||||||
|
});
|
||||||
const form = ref();
|
const form = ref();
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<Title>FreePotato Server</Title>
|
<Title>FreePotato Server</Title>
|
||||||
<Meta name="description" content="免费服务器~" />
|
<Meta name="description" content="免费服务器~" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<ElRow :gutter="10" align="middle" style="height: 900px">
|
<ElRow :gutter="10" align="middle" style="height: 900px">
|
||||||
<ElCol :span="24"><IndexNewsCarouel /></ElCol>
|
<ElCol :span="24"><IndexNewsCarouel /></ElCol>
|
||||||
<ElCol :span="24"><IndexNewsStatus /></ElCol>
|
<ElCol :span="24"><IndexNewsStatus /></ElCol>
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!--登录界面-->
|
|
||||||
<Head>
|
|
||||||
<Title>登录界面</Title>
|
|
||||||
<Meta name="description" />
|
|
||||||
</Head>
|
|
||||||
<div
|
|
||||||
style="width: 25%; margin: auto"
|
|
||||||
:style="{
|
|
||||||
boxShadow: `var(--el-box-shadow-light)`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<ClientOnly>
|
|
||||||
<Vueform
|
|
||||||
endpoint="/api/test"
|
|
||||||
method="POST"
|
|
||||||
view="tabs"
|
|
||||||
style="padding: 30px"
|
|
||||||
>
|
|
||||||
<StaticElement tag="h4" align="center" content="登录" name="static" />
|
|
||||||
<PhoneElement
|
|
||||||
input-type="number"
|
|
||||||
name="phone"
|
|
||||||
allow-incomplete
|
|
||||||
unmask
|
|
||||||
default="+86"
|
|
||||||
:include="['cn']"
|
|
||||||
label="手机号"
|
|
||||||
placeholder="+86"
|
|
||||||
:columns="{ container: 12, label: 3, wrapper: 10 }"
|
|
||||||
rules="required|min:14|max:14"
|
|
||||||
/>
|
|
||||||
<TextElement
|
|
||||||
input-type="password"
|
|
||||||
name="password"
|
|
||||||
label="密码"
|
|
||||||
rules="required|min:8|max:16|confirmed"
|
|
||||||
:columns="{ container: 12, label: 3, wrapper: 10 }"
|
|
||||||
/>
|
|
||||||
<CheckboxElement
|
|
||||||
name="policy"
|
|
||||||
label="用户协议"
|
|
||||||
rules="required"
|
|
||||||
message="您必须同意用户政策才能继续"
|
|
||||||
>
|
|
||||||
已阅读并同意<ElLink href="/" target="_blank">《用户协议》</ElLink>
|
|
||||||
</CheckboxElement>
|
|
||||||
<CheckboxElement name="news"> 希望获取最新资讯 </CheckboxElement>
|
|
||||||
<ButtonElement
|
|
||||||
name="submit"
|
|
||||||
button-label="登录"
|
|
||||||
align="center"
|
|
||||||
size="lg"
|
|
||||||
submits
|
|
||||||
/>
|
|
||||||
</Vueform>
|
|
||||||
</ClientOnly>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import PhoneElemen from "@vueform/vueform";
|
|
||||||
const form = ref();
|
|
||||||
</script>
|
|
@ -4,3 +4,8 @@
|
|||||||
<Meta name="description" />
|
<Meta name="description" />
|
||||||
</Head>
|
</Head>
|
||||||
</template>
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ["auth"],
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
111
pages/user/login/index.vue
Normal file
111
pages/user/login/index.vue
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<!--登录界面-->
|
||||||
|
<Head>
|
||||||
|
<Title>登录界面</Title>
|
||||||
|
<Meta name="description" />
|
||||||
|
</Head>
|
||||||
|
<div
|
||||||
|
class="formbox"
|
||||||
|
:style="{
|
||||||
|
boxShadow: `var(--el-box-shadow-light)`,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<ClientOnly>
|
||||||
|
<Vueform
|
||||||
|
endpoint="/api/user/login"
|
||||||
|
method="POST"
|
||||||
|
view="tabs"
|
||||||
|
style="padding: 30px"
|
||||||
|
@success="PostSuccess"
|
||||||
|
>
|
||||||
|
<StaticElement tag="h4" align="center" content="登录" name="static" />
|
||||||
|
<TextElement
|
||||||
|
ref="phone$"
|
||||||
|
name="phone"
|
||||||
|
allow-incomplete
|
||||||
|
label="手机号"
|
||||||
|
placeholder="+86"
|
||||||
|
:columns="{ container: 12, label: 3, wrapper: 10 }"
|
||||||
|
rules="required|min:11|max:11"
|
||||||
|
/>
|
||||||
|
<TextElement
|
||||||
|
input-type="password"
|
||||||
|
name="password"
|
||||||
|
allow-incomplete
|
||||||
|
label="密码"
|
||||||
|
rules="required|min:8|max:16"
|
||||||
|
:columns="{ container: 12, label: 3, wrapper: 10 }"
|
||||||
|
/>
|
||||||
|
<HiddenElement :default="ip" name="client_ip" />
|
||||||
|
<CheckboxElement
|
||||||
|
name="policy"
|
||||||
|
label="用户协议"
|
||||||
|
rules="required"
|
||||||
|
message="您必须同意用户政策才能继续"
|
||||||
|
>
|
||||||
|
已阅读并同意<ElLink href="/" target="_blank">《用户协议》</ElLink>
|
||||||
|
</CheckboxElement>
|
||||||
|
<ButtonElement
|
||||||
|
name="submit"
|
||||||
|
button-label="登录"
|
||||||
|
align="center"
|
||||||
|
size="lg"
|
||||||
|
submits
|
||||||
|
/>
|
||||||
|
</Vueform>
|
||||||
|
</ClientOnly>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PhoneElemen from "@vueform/vueform";
|
||||||
|
definePageMeta({
|
||||||
|
middleware: ["unauth"],
|
||||||
|
});
|
||||||
|
const form = ref();
|
||||||
|
const data: { query: string } = await $fetch("http://ip-api.com/json");
|
||||||
|
const ip = data.query;
|
||||||
|
function PostSuccess(response: {
|
||||||
|
data: {
|
||||||
|
code: number;
|
||||||
|
msg: string;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
}) {
|
||||||
|
if (response.data.code == 1) {
|
||||||
|
ElMessage({ message: response.data.msg, type: "success" });
|
||||||
|
const auth = useCookie("auth", { maxAge: 60 * 30 });
|
||||||
|
auth.value = response.data.token;
|
||||||
|
location.reload();
|
||||||
|
navigateTo("/", { replace: true });
|
||||||
|
} else if (response.data.code == -1) {
|
||||||
|
ElMessage({ message: response.data.msg, type: "warning" });
|
||||||
|
navigateTo("/user/register");
|
||||||
|
} else {
|
||||||
|
ElMessage({ message: response.data.msg, type: "warning" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.formbox {
|
||||||
|
width: 25%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 901px) and (max-width: 1200px) {
|
||||||
|
.formbox {
|
||||||
|
width: 50%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 601px) and (max-width: 900px) {
|
||||||
|
.formbox {
|
||||||
|
width: 75%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.formbox {
|
||||||
|
width: 88%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
9
pages/user/logout.ts
Normal file
9
pages/user/logout.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const auth = useCookie("auth");
|
||||||
|
$fetch("/api/user/logout", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
auth: auth.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
auth.value = undefined;
|
||||||
|
navigateTo("/");
|
@ -17,6 +17,7 @@
|
|||||||
view="tabs"
|
view="tabs"
|
||||||
:model-value="form"
|
:model-value="form"
|
||||||
@update:model-value="form = $event"
|
@update:model-value="form = $event"
|
||||||
|
@success="RegSuccess"
|
||||||
validate-on="change"
|
validate-on="change"
|
||||||
style="padding: 5%"
|
style="padding: 5%"
|
||||||
>
|
>
|
||||||
@ -31,18 +32,14 @@
|
|||||||
rules="required|min:3|max:64"
|
rules="required|min:3|max:64"
|
||||||
@input="Check()"
|
@input="Check()"
|
||||||
/>
|
/>
|
||||||
<PhoneElement
|
<TextElement
|
||||||
ref="phone$"
|
ref="phone$"
|
||||||
input-type="number"
|
|
||||||
name="phone"
|
name="phone"
|
||||||
allow-incomplete
|
allow-incomplete
|
||||||
unmask
|
|
||||||
default="+86"
|
|
||||||
:include="['cn']"
|
|
||||||
label="手机号"
|
label="手机号"
|
||||||
placeholder="+86"
|
placeholder="+86"
|
||||||
:columns="{ container: 12, label: 3, wrapper: 10 }"
|
:columns="{ container: 12, label: 3, wrapper: 10 }"
|
||||||
rules="required|min:14|max:14"
|
rules="required|min:11|max:11"
|
||||||
@input="Check()"
|
@input="Check()"
|
||||||
:disabled="phoneChecked"
|
:disabled="phoneChecked"
|
||||||
/>
|
/>
|
||||||
@ -95,7 +92,6 @@
|
|||||||
>
|
>
|
||||||
已阅读并同意<ElLink href="/" target="_blank">《用户协议》</ElLink>
|
已阅读并同意<ElLink href="/" target="_blank">《用户协议》</ElLink>
|
||||||
</CheckboxElement>
|
</CheckboxElement>
|
||||||
<CheckboxElement name="news"> 希望获取最新资讯 </CheckboxElement>
|
|
||||||
<ButtonElement
|
<ButtonElement
|
||||||
:disabled="canSubmit"
|
:disabled="canSubmit"
|
||||||
name="submit"
|
name="submit"
|
||||||
@ -134,30 +130,33 @@ function sliderHandleError() {
|
|||||||
function Check() {
|
function Check() {
|
||||||
const query = {
|
const query = {
|
||||||
username: form.value.username,
|
username: form.value.username,
|
||||||
phone: form.value.phone.replace("+86", ""),
|
phone: form.value.phone || "",
|
||||||
};
|
};
|
||||||
if (!/^(?!1(4|7)\d{9})1[3-9]\d{9}$/.test(query.phone)) {
|
if (!/^(?!1(4|7)\d{9})1[3-9]\d{9}$/.test(query.phone) && query.phone != "") {
|
||||||
phone$.value.messageBag.clear("errors");
|
username$.value.messageBag.clear("errors");
|
||||||
phone$.value.messageBag.append("手机号格式错误" + query.phone);
|
username$.value.messageBag.append("手机号格式错误" + query.phone);
|
||||||
canSubmit.value = true;
|
canSubmit.value = true;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
phone$.value.messageBag = [];
|
username$.value.messageBag.clear("errors");
|
||||||
}
|
}
|
||||||
$fetch("/api/test/reg", {
|
$fetch("/api/reg", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: query,
|
body: query,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (!res.phone) {
|
if (!res.phone) {
|
||||||
phone$.value.messageBag.append("手机号已经注册");
|
username$.value.messageBag.append("手机号已经注册");
|
||||||
canSubmit.value = true;
|
canSubmit.value = true;
|
||||||
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
username$.value.messageBag.clear("errors");
|
||||||
canSubmit.value = false;
|
canSubmit.value = false;
|
||||||
}
|
}
|
||||||
if (!res.username) {
|
if (!res.username) {
|
||||||
username$.value.messageBag.append("昵称已存在");
|
username$.value.messageBag.append("昵称已存在");
|
||||||
canSubmit.value = true;
|
canSubmit.value = true;
|
||||||
} else {
|
} else {
|
||||||
|
username$.value.messageBag.clear("errors");
|
||||||
canSubmit.value = false;
|
canSubmit.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -170,10 +169,10 @@ function Send() {
|
|||||||
Check();
|
Check();
|
||||||
|
|
||||||
if (!canSubmit.value) {
|
if (!canSubmit.value) {
|
||||||
$fetch("/api/test/sms", {
|
$fetch("/api/reg/sms", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
phone: form.value.phone.replace("+86", ""),
|
phone: form.value.phone,
|
||||||
},
|
},
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
switch (res.code) {
|
switch (res.code) {
|
||||||
@ -199,7 +198,7 @@ function Send() {
|
|||||||
}
|
}
|
||||||
function CheckCode() {
|
function CheckCode() {
|
||||||
if (!code$.value.invalid) {
|
if (!code$.value.invalid) {
|
||||||
$fetch("/api/test/code", {
|
$fetch("/api/reg/code", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: {
|
body: {
|
||||||
phone: form.value.phone.replace("+86", ""),
|
phone: form.value.phone.replace("+86", ""),
|
||||||
@ -217,7 +216,7 @@ function CheckCode() {
|
|||||||
ElMessage({ message: res.msg, type: "success" });
|
ElMessage({ message: res.msg, type: "success" });
|
||||||
codeBtnRef.value.disabled = true;
|
codeBtnRef.value.disabled = true;
|
||||||
codeBtnRef.value.text = "已验证";
|
codeBtnRef.value.text = "已验证";
|
||||||
phoneChecked.value = ture;
|
phoneChecked.value = true;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ElMessage({ message: res.msg, type: "warning" });
|
ElMessage({ message: res.msg, type: "warning" });
|
||||||
@ -231,6 +230,14 @@ function CheckCode() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function RegSuccess(response: { data: { code: number; msg: string } }, form$) {
|
||||||
|
if (response.data.code == 1) {
|
||||||
|
ElMessage({ message: response.data.msg, type: "success" });
|
||||||
|
navigateTo("/user/login");
|
||||||
|
} else {
|
||||||
|
ElMessage({ message: response.data.msg, type: "warning" });
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
.formbox {
|
.formbox {
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
@ -43,10 +44,10 @@ model Register {
|
|||||||
code String
|
code String
|
||||||
}
|
}
|
||||||
model Loginlogs {
|
model Loginlogs {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
phone String
|
outtime DateTime
|
||||||
time DateTime
|
|
||||||
ip String
|
ip String
|
||||||
loiner User @relation(fields: [userid], references: [id])
|
loginer User @relation(fields: [userid], references: [id])
|
||||||
userid Int
|
userid Int
|
||||||
|
token String @unique
|
||||||
}
|
}
|
38
server/api/admin/loginlogs.ts
Normal file
38
server/api/admin/loginlogs.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import type { LoginLog } from "~/types/Log";
|
||||||
|
const db = new PrismaClient();
|
||||||
|
type log = { loginer: { username: string | null } } & {
|
||||||
|
id: number;
|
||||||
|
outtime: Date;
|
||||||
|
ip: string;
|
||||||
|
userid: number;
|
||||||
|
token: string;
|
||||||
|
};
|
||||||
|
function formatLogs(raw: log[]): LoginLog[] {
|
||||||
|
var logs: LoginLog[] = [];
|
||||||
|
raw.forEach((element: log) => {
|
||||||
|
logs.push({
|
||||||
|
id: element.id,
|
||||||
|
username: element.loginer.username || "none",
|
||||||
|
date: new Date(element.outtime.getTime() - 1800000).toLocaleTimeString(),
|
||||||
|
ip: element.ip,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const loginlogs = await db.loginlogs.findMany({
|
||||||
|
orderBy: {
|
||||||
|
outtime: "desc", // 'asc' 表示升序,'desc' 表示降序
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
loginer: {
|
||||||
|
select: {
|
||||||
|
username: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await db.$disconnect();
|
||||||
|
return formatLogs(loginlogs);
|
||||||
|
});
|
@ -14,6 +14,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
code: "",
|
code: "",
|
||||||
deadline: new Date(),
|
deadline: new Date(),
|
||||||
};
|
};
|
||||||
|
await db.$disconnect();
|
||||||
const deadlineDate = new Date(register.deadline);
|
const deadlineDate = new Date(register.deadline);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (now > deadlineDate) {
|
if (now > deadlineDate) {
|
@ -2,7 +2,7 @@ import { PrismaClient } from "@prisma/client";
|
|||||||
const db = new PrismaClient();
|
const db = new PrismaClient();
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
return {
|
const res = {
|
||||||
username:
|
username:
|
||||||
(await db.user.findFirst({
|
(await db.user.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@ -16,4 +16,6 @@ export default defineEventHandler(async (event) => {
|
|||||||
},
|
},
|
||||||
})) == null,
|
})) == null,
|
||||||
};
|
};
|
||||||
|
await db.$disconnect();
|
||||||
|
return res;
|
||||||
});
|
});
|
@ -83,11 +83,14 @@ export default defineEventHandler(async (event) => {
|
|||||||
const deadlineDate = new Date(register.deadline);
|
const deadlineDate = new Date(register.deadline);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (now <= deadlineDate) {
|
if (now <= deadlineDate) {
|
||||||
|
await db.$disconnect();
|
||||||
return { code: 2, msg: "三分钟内请勿重发送" };
|
return { code: 2, msg: "三分钟内请勿重发送" };
|
||||||
} else {
|
} else {
|
||||||
|
await db.$disconnect();
|
||||||
return send(body.phone);
|
return send(body.phone);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
await db.$disconnect();
|
||||||
return send(body.phone);
|
return send(body.phone);
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -2,37 +2,6 @@ import { PrismaClient } from "@prisma/client";
|
|||||||
|
|
||||||
const db = new PrismaClient();
|
const db = new PrismaClient();
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler((event) => {
|
||||||
const body = await readBody(event);
|
return event;
|
||||||
console.info(body);
|
|
||||||
let isSend =
|
|
||||||
(await db.register.findFirst({
|
|
||||||
where: {
|
|
||||||
phone: body.phone,
|
|
||||||
},
|
|
||||||
})) != null;
|
|
||||||
if (isSend) {
|
|
||||||
const register = (await db.register.findFirst({
|
|
||||||
where: {
|
|
||||||
phone: body.phone,
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
deadline: "desc", // 'asc' 表示升序,'desc' 表示降序
|
|
||||||
},
|
|
||||||
})) || {
|
|
||||||
deadline: new Date(),
|
|
||||||
};
|
|
||||||
const deadlineDate = new Date(register.deadline);
|
|
||||||
const now = new Date();
|
|
||||||
if (now <= deadlineDate) {
|
|
||||||
return { code: 2, msg: "三分钟内请勿重发送" };
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
msg:
|
|
||||||
"发送成功" + now + "<=" + deadlineDate + ":" + (now <= deadlineDate),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return isSend;
|
|
||||||
});
|
});
|
||||||
|
35
server/api/user/auth.ts
Normal file
35
server/api/user/auth.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
|
const db = new PrismaClient();
|
||||||
|
|
||||||
|
async function auth(auth: string) {
|
||||||
|
const res = await db.loginlogs.findFirst({
|
||||||
|
where: { token: auth.toString() },
|
||||||
|
});
|
||||||
|
//return JSON.stringify((await res).values)
|
||||||
|
if (res == null) {
|
||||||
|
return {
|
||||||
|
login: false,
|
||||||
|
code: 0, //未登录
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const deadlineDate = new Date(res.outtime);
|
||||||
|
const now = new Date();
|
||||||
|
if (now < deadlineDate) {
|
||||||
|
return {
|
||||||
|
login: true,
|
||||||
|
code: 1, //登录状态正常
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
login: false,
|
||||||
|
code: 2, //登录超时
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event);
|
||||||
|
return auth(body.auth);
|
||||||
|
});
|
@ -2,36 +2,43 @@ import { PrismaClient } from "@prisma/client";
|
|||||||
const db = new PrismaClient();
|
const db = new PrismaClient();
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
const register = (await db.register.findFirst({
|
const register = await db.register.findFirst({
|
||||||
where: {
|
where: {
|
||||||
phone: body.phone,
|
phone: body.phone,
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: {
|
||||||
deadline: "desc", // 'asc' 表示升序,'desc' 表示降序
|
deadline: "desc", // 'asc' 表示升序,'desc' 表示降序
|
||||||
},
|
},
|
||||||
})) || {
|
});
|
||||||
code: "",
|
if (register != null) {
|
||||||
deadline: new Date(),
|
const deadlineDate = new Date(register.deadline.getTime() + 7 * 60 * 1000);
|
||||||
};
|
const now = new Date();
|
||||||
const deadlineDate = new Date(register.deadline.getTime() + 7 * 60 * 1000);
|
if (now <= deadlineDate) {
|
||||||
const now = new Date();
|
if (register.code != body.code) {
|
||||||
if (now <= deadlineDate) {
|
return { code: 0, msg: "验证码错误请重新发送" + register };
|
||||||
if (register.code != body.code) {
|
} else {
|
||||||
return { code: 0, msg: "验证码错误请重新发送" };
|
await db.user.create({
|
||||||
|
data: {
|
||||||
|
username: body.username,
|
||||||
|
phone: body.phone,
|
||||||
|
password: (await import("crypto"))
|
||||||
|
.createHash("md5")
|
||||||
|
.update(body.password)
|
||||||
|
.digest("hex"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await db.$disconnect();
|
||||||
|
return { code: 1, msg: "注册成功" };
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await db.user.create({
|
await db.$disconnect();
|
||||||
data: {
|
return {
|
||||||
username: body.username,
|
code: -1,
|
||||||
phone: body.phone,
|
msg: "验证码超时",
|
||||||
password: (await import("crypto"))
|
};
|
||||||
.createHash("md5")
|
|
||||||
.update(body.password)
|
|
||||||
.digest("hex"),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return { code: 1, msg: "注册成功" };
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
await db.$disconnect();
|
||||||
return {
|
return {
|
||||||
code: -1,
|
code: -1,
|
||||||
msg: "验证码超时",
|
msg: "验证码超时",
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import { PrismaClient } from "@prisma/client";
|
import { PrismaClient } from "@prisma/client";
|
||||||
const db = new PrismaClient();
|
const db = new PrismaClient();
|
||||||
|
function generateDeadline() {
|
||||||
|
const now = new Date(); // 获取当前时间
|
||||||
|
const threeMinutesLater = new Date(now.getTime() + 30 * 60 * 1000); // 加上30分钟
|
||||||
|
return threeMinutesLater.toISOString(); // 转换为ISO格式字符串
|
||||||
|
}
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
const loginer = await db.user.findFirst({
|
const loginer = await db.user.findFirst({
|
||||||
@ -17,9 +22,26 @@ export default defineEventHandler(async (event) => {
|
|||||||
.update(body.password)
|
.update(body.password)
|
||||||
.digest("hex")
|
.digest("hex")
|
||||||
) {
|
) {
|
||||||
return { code: 1, msg: "登录成功" };
|
const token = (await import("crypto"))
|
||||||
|
.createHash("md5")
|
||||||
|
.update(body.password + new Date().toISOString())
|
||||||
|
.digest("hex");
|
||||||
|
await db.loginlogs.create({
|
||||||
|
data: {
|
||||||
|
userid: loginer.id,
|
||||||
|
outtime: generateDeadline(),
|
||||||
|
token: token,
|
||||||
|
ip: body.client_ip,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await db.$disconnect();
|
||||||
|
return { code: 1, msg: "登录成功", token: token };
|
||||||
} else {
|
} else {
|
||||||
return { code: 0, msg: "用户名、手机号或密码错误" };
|
await db.$disconnect();
|
||||||
|
return {
|
||||||
|
code: 0,
|
||||||
|
msg: "用户名、手机号或密码错误" /* + JSON.stringify(body) */,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
15
server/api/user/logout.ts
Normal file
15
server/api/user/logout.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
const db = new PrismaClient();
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event);
|
||||||
|
await db.loginlogs.update({
|
||||||
|
where: {
|
||||||
|
token: body.auth,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
outtime: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await db.$disconnect();
|
||||||
|
return 1;
|
||||||
|
});
|
6
types/Log/LoginLog.ts
Normal file
6
types/Log/LoginLog.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export type LoginLog = {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
date: string;
|
||||||
|
ip: string;
|
||||||
|
};
|
1
types/Log/index.d.ts
vendored
Normal file
1
types/Log/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { LoginLog } from "./LoginLog.ts";
|
Loading…
Reference in New Issue
Block a user