diff --git a/src/components/pages/auth/signup/(hooks)/use-signup-form.ts b/src/components/pages/auth/signup/(hooks)/use-signup-form.ts index 823e25a..52a5ecf 100644 --- a/src/components/pages/auth/signup/(hooks)/use-signup-form.ts +++ b/src/components/pages/auth/signup/(hooks)/use-signup-form.ts @@ -1,12 +1,14 @@ +import { AppContext } from "@/src/pages/_app"; import { TurboCISignupFormObject } from "@/src/types"; import useStatus from "@/twui/components/hooks/useStatus"; -import { useState } from "react"; +import { useContext, useState } from "react"; type Params = { new_deployment_user?: boolean; }; export default function useSignupForm({ new_deployment_user }: Params) { + const { pageProps } = useContext(AppContext); const [newUser, setNewUser] = useState({}); const { loading, setLoading } = useStatus(); const [isPasswordConfirmed, setIsPasswordConfirmed] = useState(false); @@ -18,5 +20,6 @@ export default function useSignupForm({ new_deployment_user }: Params) { setLoading, isPasswordConfirmed, setIsPasswordConfirmed, + pageProps, }; } diff --git a/src/components/pages/auth/signup/(partials)/signup-form.tsx b/src/components/pages/auth/signup/(partials)/signup-form.tsx index adb5e77..7e4b3a2 100644 --- a/src/components/pages/auth/signup/(partials)/signup-form.tsx +++ b/src/components/pages/auth/signup/(partials)/signup-form.tsx @@ -19,6 +19,7 @@ export default function SignupForm({ new_deployment_user }: Props) { setLoading, isPasswordConfirmed, setIsPasswordConfirmed, + pageProps, } = useSignupForm({ new_deployment_user }); const is_password_valid = Boolean( @@ -28,110 +29,146 @@ export default function SignupForm({ new_deployment_user }: Props) { ); return ( - - { - setNewUser((prev) => ({ - ...prev, - first_name: v, - })); - }} - showLabel - /> - { - setNewUser((prev) => ({ - ...prev, - last_name: v, - })); - }} - showLabel - /> - { - setNewUser((prev) => ({ - ...prev, - email: v, - })); - }} - showLabel - /> - { - setNewUser((prev) => ({ - ...prev, - password: v, - })); - }} - validity={{ - isValid: - !Boolean(newUser.password?.match(/./)) || - !Boolean(newUser.confirmed_password?.match(/./)) - ? true - : is_password_valid, - msg: `Passwords don't match`, - }} - showLabel - /> - { - setNewUser((prev) => ({ - ...prev, - confirmed_password: v, - })); +
{ + e.preventDefault(); + }} + > + + { + setNewUser((prev) => ({ + ...prev, + first_name: v, + })); + }} + required + showLabel + /> + { + setNewUser((prev) => ({ + ...prev, + last_name: v, + })); + }} + showLabel + /> + { + setNewUser((prev) => ({ + ...prev, + email: v, + })); + }} + required + showLabel + /> + {pageProps.user.id ? ( + { + setNewUser((prev) => ({ + ...prev, + username: v, + })); + }} + validationRegex={/^[a-z0-9\-]{3,}$/} + info={ + <> + Allowed characters:{" "} + + a-z, 0-9, - + + . + + } + wrapperWrapperProps={{ + className: "items-start!", + }} + required + showLabel + /> + ) : null} + { + setNewUser((prev) => ({ + ...prev, + password: v, + })); + }} + validity={{ + isValid: + !Boolean(newUser.password?.match(/./)) || + !Boolean(newUser.confirmed_password?.match(/./)) + ? true + : is_password_valid, + msg: `Passwords don't match`, + }} + required + showLabel + /> + { + setNewUser((prev) => ({ + ...prev, + confirmed_password: v, + })); - setIsPasswordConfirmed(v == newUser.password); - }} - showLabel - /> - - + if (res.success) { + window.location.reload(); + } + }) + .finally(() => {}); + }} + loading={loading} + > + Signup + + +
); } diff --git a/src/functions/deployment-users/setup-deployment-user.ts b/src/functions/deployment-users/setup-deployment-user.ts new file mode 100644 index 0000000..289a8e2 --- /dev/null +++ b/src/functions/deployment-users/setup-deployment-user.ts @@ -0,0 +1,35 @@ +import { NSQLITE_TURBOCI_ADMIN_USERS, NSQLiteTables } from "@/src/db/types"; +import NSQLite from "@moduletrace/nsqlite"; +import { existsSync } from "fs"; + +type Params = { + user_id: string | number; +}; + +export default async function setupDeploymentUser({ user_id }: Params) { + const target_user_res = await NSQLite.select< + NSQLITE_TURBOCI_ADMIN_USERS, + (typeof NSQLiteTables)[number] + >({ + table: "users", + }); + + const target_user = target_user_res.singleRes; + + if (!target_user?.id) { + return; + } + + const { username } = target_user; + + const user_dir = `/home/${username}`; + + if (!existsSync(user_dir)) { + let cmd = ``; + + cmd += `useradd --create-home --shell /bin/bash --comment "TurboCI Deployment user ${username}" ${username}\n`; + cmd += `passwd --lock "${username}"\n`; + } + + return; +} diff --git a/src/pages/api/auth/signup.ts b/src/pages/api/auth/signup.ts index a302627..a45fd6e 100644 --- a/src/pages/api/auth/signup.ts +++ b/src/pages/api/auth/signup.ts @@ -6,6 +6,7 @@ import { APIResponseObject } from "@moduletrace/datasquirel/dist/package-shared/ import NSQLite from "@moduletrace/nsqlite"; import type { NextApiRequest, NextApiResponse } from "next"; import userAuth from "@/src/utils/user-auth"; +import { slugify } from "@/src/exports/client-exports"; export default async function handler( req: NextApiRequest, @@ -23,6 +24,10 @@ export default async function handler( const { new_user } = req.body as APIReqObject; + if (user?.id && !new_user?.username?.match(/^[a-z0-9\-]{3,}$/)) { + throw new Error(`Please pass a valid username`); + } + if (!new_user) { throw new Error(`No new User Object Passed!`); } @@ -52,7 +57,7 @@ export default async function handler( }); } - const { first_name, email, last_name, password } = new_user; + const { first_name, email, last_name, password, username } = new_user; const new_user_password = hashPassword({ password }); @@ -67,6 +72,7 @@ export default async function handler( email, password: new_user_password, is_super_admin: user?.id ? 0 : 1, + username: slugify(username), }, ], table: "users", diff --git a/src/types/index.ts b/src/types/index.ts index 4f50b02..8a75b65 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -230,6 +230,7 @@ export type TurboCISignupFormObject = { email?: string; password?: string; confirmed_password?: string; + username?: string; }; export type TurboCIAdminAppContextType = ReturnType;