feat: add authentication

This commit is contained in:
2024-03-16 07:52:42 +07:00
parent ed5c715429
commit 1e8252d9da
28 changed files with 8519 additions and 138 deletions
+25 -10
View File
@@ -1,5 +1,5 @@
import React from "react";
import { Slot } from "expo-router";
import React, { useEffect, useState } from "react";
import { Slot, router, usePathname } from "expo-router";
import { QueryClientProvider } from "react-query";
import queryClient from "@/lib/queryClient";
import { View } from "react-native";
@@ -7,22 +7,37 @@ import { cn, tw } from "@/lib/utils";
import { useDeviceContext } from "twrnc";
import { StatusBar } from "expo-status-bar";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useStore } from "zustand";
import authStore from "@/stores/authStore";
const RootLayout = () => {
const insets = useSafeAreaInsets();
const pathname = usePathname();
const isLoggedIn = useStore(authStore, (i) => i.isLoggedIn);
const [isLoaded, setLoaded] = useState(false);
useDeviceContext(tw);
useEffect(() => {
if (!isLoaded) {
return;
}
if (!isLoggedIn && !pathname.startsWith("/auth")) {
router.navigate("/auth/login");
return;
}
}, [pathname, isLoggedIn, isLoaded]);
useEffect(() => {
setLoaded(true);
}, []);
return (
<QueryClientProvider client={queryClient}>
<StatusBar style="auto" />
<View style={cn("flex-1 bg-[#f2f7fb]")}>
<View
style={cn("flex-1 mx-auto w-full max-w-xl", {
paddingTop: insets.top,
})}
>
<Slot />
</View>
<View style={cn("flex-1 bg-[#f2f7fb]", { paddingTop: insets.top })}>
<Slot />
</View>
</QueryClientProvider>
);
+77
View File
@@ -0,0 +1,77 @@
import React from "react";
import Box from "@ui/Box";
import Container from "@ui/Container";
import Text from "@ui/Text";
import { ScrollView } from "react-native";
import { cn } from "@/lib/utils";
import Input from "@ui/Input";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import Button from "@ui/Button";
import { useMutation } from "react-query";
import api from "@/lib/api";
import Alert from "@ui/Alert";
import { setAuthToken } from "@/stores/authStore";
import { router } from "expo-router";
const schema = z.object({
username: z.string().min(1),
password: z.string().min(1),
});
type FormSchema = z.infer<typeof schema>;
const defaultValues: FormSchema = {
username: "",
password: "",
};
const LoginPage = () => {
const form = useForm({ resolver: zodResolver(schema), defaultValues });
const login = useMutation({
mutationFn: (json: FormSchema) =>
api.auth.login.$post({ json }).then((res) => res.json()),
onSuccess: async (data) => {
setAuthToken(data.token);
router.navigate("/");
},
});
return (
<ScrollView
contentContainerStyle={cn("flex-1 flex items-center justify-center")}
>
<Container className="p-4">
<Box className="p-8 bg-white rounded-lg">
<Text className="text-2xl">Login</Text>
<Input
label="Username"
form={form}
path="username"
className="mt-6"
/>
<Input
label="Password"
form={form}
path="password"
className="mt-4"
secureTextEntry
/>
<Alert className="mt-4" error={login.error} />
<Button
className="mt-8"
onPress={form.handleSubmit((val) => login.mutate(val))}
>
Login
</Button>
</Box>
</Container>
</ScrollView>
);
};
export default LoginPage;
@@ -6,7 +6,6 @@ import Text from "@ui/Text";
import { HStack, VStack } from "@ui/Stack";
import Box from "@ui/Box";
import ProcessList from "./ProcessList";
import Divider from "@ui/Divider";
import Button from "@ui/Button";
import { Ionicons } from "@ui/Icons";
+12 -9
View File
@@ -5,28 +5,31 @@ import Text from "@ui/Text";
import Performance from "./_sections/Performance";
import Summary from "./_sections/Summary";
import Storage from "./_sections/Storage";
import { ScrollView } from "react-native";
import { cn } from "@/lib/utils";
import Container from "@ui/Container";
import { useAuth } from "@/stores/authStore";
const App = () => {
const HomePage = () => {
const { isLoggedIn } = useAuth();
const { data: system } = useQuery({
queryKey: ["system"],
queryFn: () => api.system.$get().then((r) => r.json()),
refetchInterval: 1000,
enabled: isLoggedIn,
});
if (!isLoggedIn) {
return null;
}
return (
<ScrollView
contentContainerStyle={cn("px-4 py-8 md:py-16")}
showsVerticalScrollIndicator={false}
>
<Container scrollable className="px-4 py-8 md:py-16">
<Text className="text-2xl font-medium">Home Lab</Text>
<Summary data={system} />
<Performance data={system} />
<Storage data={system} />
</ScrollView>
</Container>
);
};
export default App;
export default HomePage;