Updates
This commit is contained in:
parent
a34fd3aa20
commit
ee62ac8f54
2
.gitignore
vendored
2
.gitignore
vendored
@ -43,3 +43,5 @@ next-env.d.ts
|
|||||||
.backups
|
.backups
|
||||||
/secrets
|
/secrets
|
||||||
.tmp
|
.tmp
|
||||||
|
|
||||||
|
/test
|
||||||
3
bun.lock
3
bun.lock
@ -19,6 +19,7 @@
|
|||||||
"next": "14^",
|
"next": "14^",
|
||||||
"next-mdx-remote": "^6.0.0",
|
"next-mdx-remote": "^6.0.0",
|
||||||
"openai": "^6.25.0",
|
"openai": "^6.25.0",
|
||||||
|
"prism-themes": "^1.9.0",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-code-blocks": "^0.1.6",
|
"react-code-blocks": "^0.1.6",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
@ -808,6 +809,8 @@
|
|||||||
|
|
||||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
||||||
|
|
||||||
|
"prism-themes": ["prism-themes@1.9.0", "", {}, "sha512-tX2AYsehKDw1EORwBps+WhBFKc2kxfoFpQAjxBndbZKr4fRmMkv47XN0BghC/K1qwodB1otbe4oF23vUTFDokw=="],
|
||||||
|
|
||||||
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
|
||||||
|
|
||||||
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
"process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
"next": "14^",
|
"next": "14^",
|
||||||
"next-mdx-remote": "^6.0.0",
|
"next-mdx-remote": "^6.0.0",
|
||||||
"openai": "^6.25.0",
|
"openai": "^6.25.0",
|
||||||
|
"prism-themes": "^1.9.0",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-code-blocks": "^0.1.6",
|
"react-code-blocks": "^0.1.6",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
|
|||||||
10
src/components/general/code-block.tsx
Normal file
10
src/components/general/code-block.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import RemoteCodeBlock from "@/twui/components/elements/RemoteCodeBlock";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
content: string;
|
||||||
|
mode: "javascript" | "typescript" | "sh";
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function TurboCICodeBlock({ mode, content }: Props) {
|
||||||
|
return <RemoteCodeBlock content={`\`\`\`${mode}\n${content}\n\`\`\``} />;
|
||||||
|
}
|
||||||
@ -13,10 +13,12 @@ import useSettingsForm from "../(hooks)/use-settings-form";
|
|||||||
import { APIReqObject } from "@/src/types";
|
import { APIReqObject } from "@/src/types";
|
||||||
|
|
||||||
export default function SettingsForm() {
|
export default function SettingsForm() {
|
||||||
const { setToast } = useContext(AppContext);
|
const { setToast, pageProps } = useContext(AppContext);
|
||||||
const { formData, setFormData, loading, setLoading, user } =
|
const { formData, setFormData, loading, setLoading, user } =
|
||||||
useSettingsForm();
|
useSettingsForm();
|
||||||
|
|
||||||
|
const deployment_user = pageProps.deployment_user;
|
||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
if (!formData.first_name?.match(/./)) return;
|
if (!formData.first_name?.match(/./)) return;
|
||||||
|
|
||||||
@ -54,6 +56,10 @@ export default function SettingsForm() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!deployment_user?.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack className="w-full max-w-[600px] items-stretch gap-6">
|
<Stack className="w-full max-w-[600px] items-stretch gap-6">
|
||||||
<Stack className="gap-1">
|
<Stack className="gap-1">
|
||||||
@ -62,7 +68,9 @@ export default function SettingsForm() {
|
|||||||
</Span>
|
</Span>
|
||||||
<Span>
|
<Span>
|
||||||
<strong>
|
<strong>
|
||||||
{user.super_admin ? "root" : user?.username || "—"}
|
{user.super_admin
|
||||||
|
? "root"
|
||||||
|
: deployment_user?.username || "—"}
|
||||||
</strong>
|
</strong>
|
||||||
</Span>
|
</Span>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@ -26,6 +26,10 @@ export default function DeleteDeplotmentUserButton({
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (dep_user.is_super_admin) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -0,0 +1,51 @@
|
|||||||
|
import { useContext } from "react";
|
||||||
|
import { AppContext } from "@/src/pages/_app";
|
||||||
|
import Stack from "@/twui/components/layout/Stack";
|
||||||
|
import H2 from "@/twui/components/layout/H2";
|
||||||
|
import Span from "@/twui/components/layout/Span";
|
||||||
|
import RemoteCodeBlock from "@/twui/components/elements/RemoteCodeBlock";
|
||||||
|
import SingleLineCodeBlock from "@/twui/components/elements/SingleLineCodeBlock";
|
||||||
|
import TurboCICodeBlock from "@/src/components/general/code-block";
|
||||||
|
import H3 from "@/twui/components/layout/H3";
|
||||||
|
|
||||||
|
export default function SSHConnection() {
|
||||||
|
const { pageProps } = useContext(AppContext);
|
||||||
|
const { deployment_user, deployment } = pageProps;
|
||||||
|
|
||||||
|
if (!deployment_user?.id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const is_super_admin = Boolean(deployment_user.is_super_admin);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack className="grid-cell-content">
|
||||||
|
<H2>Connection</H2>
|
||||||
|
<Span>
|
||||||
|
Use these information to connect this user to the Relay
|
||||||
|
server
|
||||||
|
</Span>
|
||||||
|
</Stack>
|
||||||
|
<hr />
|
||||||
|
<Stack className="nested-grid-frame grid-cols-2">
|
||||||
|
<Stack className="grid-cell">
|
||||||
|
<Stack className="grid-cell-content">
|
||||||
|
<H3>Admin Port Forwarding</H3>
|
||||||
|
|
||||||
|
<Span>
|
||||||
|
Use these information to connect this user to the
|
||||||
|
Relay server
|
||||||
|
</Span>
|
||||||
|
<TurboCICodeBlock
|
||||||
|
content={`ssh -N -L ${deployment_user.username}@${deployment?.relay_server_ip}`}
|
||||||
|
mode="sh"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
<Stack className="grid-cell"></Stack>
|
||||||
|
</Stack>
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import Row from "@/twui/components/layout/Row";
|
|||||||
import SignupForm from "../../../auth/signup/(partials)/signup-form";
|
import SignupForm from "../../../auth/signup/(partials)/signup-form";
|
||||||
import Stack from "@/twui/components/layout/Stack";
|
import Stack from "@/twui/components/layout/Stack";
|
||||||
import DeleteDeplotmentUserButton from "./(partials)/delete-deployment-user-button";
|
import DeleteDeplotmentUserButton from "./(partials)/delete-deployment-user-button";
|
||||||
|
import SSHConnection from "./(sections)/ssh-connection";
|
||||||
|
|
||||||
export default function Main() {
|
export default function Main() {
|
||||||
const { pageProps } = useContext(AppContext);
|
const { pageProps } = useContext(AppContext);
|
||||||
@ -43,6 +44,8 @@ export default function Main() {
|
|||||||
<Stack className="grid-cell-content max-w-[600px]">
|
<Stack className="grid-cell-content max-w-[600px]">
|
||||||
<SignupForm existing_user={deployment_user} />
|
<SignupForm existing_user={deployment_user} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Divider />
|
||||||
|
<SSHConnection/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,57 @@
|
|||||||
|
import Input from "@/twui/components/form/Input";
|
||||||
|
import useSignupForm from "../(hooks)/use-signup-form";
|
||||||
|
|
||||||
|
type Props = ReturnType<typeof useSignupForm>;
|
||||||
|
|
||||||
|
export default function SignupFormPassword({
|
||||||
|
isPasswordConfirmed,
|
||||||
|
newUser,
|
||||||
|
setNewUser,
|
||||||
|
setIsPasswordConfirmed,
|
||||||
|
}: Props) {
|
||||||
|
const is_password_valid = Boolean(
|
||||||
|
isPasswordConfirmed &&
|
||||||
|
Boolean(newUser.password?.match(/./)) &&
|
||||||
|
Boolean(newUser.confirmed_password?.match(/./)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
placeholder="Password"
|
||||||
|
title="Password"
|
||||||
|
type="password"
|
||||||
|
changeHandler={(v) => {
|
||||||
|
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
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
placeholder="Confirm Password"
|
||||||
|
title="Confirm Password"
|
||||||
|
type="password"
|
||||||
|
changeHandler={(v) => {
|
||||||
|
setNewUser((prev) => ({
|
||||||
|
...prev,
|
||||||
|
confirmed_password: v,
|
||||||
|
}));
|
||||||
|
|
||||||
|
setIsPasswordConfirmed(v == newUser.password);
|
||||||
|
}}
|
||||||
|
showLabel
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -5,9 +5,9 @@ import useSignupForm from "../(hooks)/use-signup-form";
|
|||||||
import fetchApi from "@/twui/components/utils/fetch/fetchApi";
|
import fetchApi from "@/twui/components/utils/fetch/fetchApi";
|
||||||
import { APIReqObject } from "@/src/types";
|
import { APIReqObject } from "@/src/types";
|
||||||
import { APIResponseObject } from "@moduletrace/datasquirel/dist/package-shared/types";
|
import { APIResponseObject } from "@moduletrace/datasquirel/dist/package-shared/types";
|
||||||
import { useEffect } from "react";
|
|
||||||
import { NSQLITE_TURBOCI_ADMIN_USERS } from "@/src/db/types";
|
import { NSQLITE_TURBOCI_ADMIN_USERS } from "@/src/db/types";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import SignupFormPassword from "./signup-form-password";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
new_deployment_user?: boolean;
|
new_deployment_user?: boolean;
|
||||||
@ -18,6 +18,7 @@ export default function SignupForm({
|
|||||||
new_deployment_user,
|
new_deployment_user,
|
||||||
existing_user,
|
existing_user,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const init = useSignupForm({ new_deployment_user, existing_user });
|
||||||
const {
|
const {
|
||||||
newUser,
|
newUser,
|
||||||
setNewUser,
|
setNewUser,
|
||||||
@ -26,7 +27,7 @@ export default function SignupForm({
|
|||||||
isPasswordConfirmed,
|
isPasswordConfirmed,
|
||||||
setIsPasswordConfirmed,
|
setIsPasswordConfirmed,
|
||||||
pageProps,
|
pageProps,
|
||||||
} = useSignupForm({ new_deployment_user, existing_user });
|
} = init;
|
||||||
|
|
||||||
const is_password_valid = Boolean(
|
const is_password_valid = Boolean(
|
||||||
isPasswordConfirmed &&
|
isPasswordConfirmed &&
|
||||||
@ -80,7 +81,7 @@ export default function SignupForm({
|
|||||||
required
|
required
|
||||||
showLabel
|
showLabel
|
||||||
/>
|
/>
|
||||||
{pageProps.user.id ? (
|
{pageProps.user?.id && !existing_user?.is_super_admin ? (
|
||||||
<Input
|
<Input
|
||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
title="Username"
|
title="Username"
|
||||||
@ -115,47 +116,7 @@ export default function SignupForm({
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{existing_user?.id ? null : (
|
{existing_user?.id ? null : <SignupFormPassword {...init} />}
|
||||||
<>
|
|
||||||
<Input
|
|
||||||
placeholder="Password"
|
|
||||||
title="Password"
|
|
||||||
type="password"
|
|
||||||
changeHandler={(v) => {
|
|
||||||
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
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
placeholder="Confirm Password"
|
|
||||||
title="Confirm Password"
|
|
||||||
type="password"
|
|
||||||
changeHandler={(v) => {
|
|
||||||
setNewUser((prev) => ({
|
|
||||||
...prev,
|
|
||||||
confirmed_password: v,
|
|
||||||
}));
|
|
||||||
|
|
||||||
setIsPasswordConfirmed(v == newUser.password);
|
|
||||||
}}
|
|
||||||
showLabel
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Button
|
<Button
|
||||||
title="Login"
|
title="Login"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -163,7 +124,7 @@ export default function SignupForm({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirm_msg = pageProps.user.id
|
const confirm_msg = pageProps.user?.id
|
||||||
? `Add New User?`
|
? `Add New User?`
|
||||||
: `Create Super Admin Account?`;
|
: `Create Super Admin Account?`;
|
||||||
|
|
||||||
@ -187,20 +148,31 @@ export default function SignupForm({
|
|||||||
)
|
)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
if (pageProps.user.id) {
|
if (pageProps.user?.id) {
|
||||||
window.location.pathname = `/admin/users`;
|
window.location.pathname = `/admin/users`;
|
||||||
} else {
|
} else {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
window.alert(
|
||||||
|
res.msg ||
|
||||||
|
res.error ||
|
||||||
|
`New User Creation Failed!`,
|
||||||
|
);
|
||||||
|
console.log("res", res);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {});
|
.finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
setLoading(false);
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
>
|
>
|
||||||
{existing_user?.id
|
{existing_user?.id
|
||||||
? `Edit User`
|
? `Edit User`
|
||||||
: pageProps.user.super_admin
|
: pageProps.user?.super_admin
|
||||||
? "Add User"
|
? "Add User"
|
||||||
: "Signup"}
|
: "Signup"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -17,8 +17,6 @@ export default async function deleteDeploymentUser({ user_id }: Params) {
|
|||||||
targetId: _n(user_id),
|
targetId: _n(user_id),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("target_user_res", target_user_res);
|
|
||||||
|
|
||||||
const target_user = target_user_res.singleRes;
|
const target_user = target_user_res.singleRes;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -31,6 +29,8 @@ export default async function deleteDeploymentUser({ user_id }: Params) {
|
|||||||
|
|
||||||
const { username } = target_user;
|
const { username } = target_user;
|
||||||
|
|
||||||
|
console.log("username", username);
|
||||||
|
|
||||||
const { force_command_file, sshd_config_file } = grabDeploymentUserDirNames(
|
const { force_command_file, sshd_config_file } = grabDeploymentUserDirNames(
|
||||||
{ user: target_user },
|
{ user: target_user },
|
||||||
);
|
);
|
||||||
@ -38,12 +38,13 @@ export default async function deleteDeploymentUser({ user_id }: Params) {
|
|||||||
let cmd = `/bin/bash << 'TURBOCIHEREDOC'\n`;
|
let cmd = `/bin/bash << 'TURBOCIHEREDOC'\n`;
|
||||||
|
|
||||||
// cmd += `userdel -r ${username}\n`;
|
// cmd += `userdel -r ${username}\n`;
|
||||||
cmd += `killall -u ${username}\n`;
|
// cmd += `pkill -u ${username} || echo "User Deleted!"\n`;
|
||||||
cmd += `deluser --remove-all-files ${username}\n`;
|
cmd += `deluser --remove-all-files ${username} || echo "User Deleted!"\n`;
|
||||||
|
|
||||||
cmd += `rm -f ${force_command_file}\n`;
|
cmd += `rm -f ${force_command_file}\n`;
|
||||||
cmd += `rm -f ${sshd_config_file}\n`;
|
cmd += `rm -f ${sshd_config_file}\n`;
|
||||||
cmd += `Match User ${username}\n`;
|
|
||||||
|
cmd += `TURBOCIHEREDOC\n`;
|
||||||
|
|
||||||
execSync(cmd);
|
execSync(cmd);
|
||||||
|
|
||||||
|
|||||||
@ -18,11 +18,13 @@ export default async function setupDeploymentUser({ user_id }: Params) {
|
|||||||
targetId: _n(user_id),
|
targetId: _n(user_id),
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("target_user_res", target_user_res);
|
|
||||||
|
|
||||||
const target_user = target_user_res.singleRes;
|
const target_user = target_user_res.singleRes;
|
||||||
|
|
||||||
if (!target_user?.id || !target_user.username) {
|
if (
|
||||||
|
!target_user?.id ||
|
||||||
|
!target_user.username ||
|
||||||
|
target_user.is_super_admin
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +44,7 @@ export default async function setupDeploymentUser({ user_id }: Params) {
|
|||||||
cmd += `useradd --create-home --shell /bin/bash --comment "TurboCI Deployment user ${username}" ${username}\n`;
|
cmd += `useradd --create-home --shell /bin/bash --comment "TurboCI Deployment user ${username}" ${username}\n`;
|
||||||
cmd += `passwd --lock "${username}"\n`;
|
cmd += `passwd --lock "${username}"\n`;
|
||||||
cmd += `mkdir -p "${ssh_dir}"\n`;
|
cmd += `mkdir -p "${ssh_dir}"\n`;
|
||||||
cmd += `ssh-keygen -t ed25519 -f "${ssh_key_file}" -N ""\n`;
|
cmd += `ssh-keygen -t ed25519 -f "${ssh_key_file}" -N "" -C "${username}@$(hostname)"\n`;
|
||||||
cmd += `cp "${ssh_key_file}.pub" "${ssh_dir}/authorized_keys"\n`;
|
cmd += `cp "${ssh_key_file}.pub" "${ssh_dir}/authorized_keys"\n`;
|
||||||
|
|
||||||
cmd += `chown -R "${username}:${username}" "${ssh_dir}"\n`;
|
cmd += `chown -R "${username}:${username}" "${ssh_dir}"\n`;
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
|
import { User } from "@/src/types";
|
||||||
import { TWUI_LINK_LIST_LINK_OBJECT } from "@/twui/components/elements/LinkList";
|
import { TWUI_LINK_LIST_LINK_OBJECT } from "@/twui/components/elements/LinkList";
|
||||||
|
|
||||||
export const AdminAsideLinks: (
|
type Params = { user?: User };
|
||||||
|
|
||||||
|
export function AdminAsideLinks({ user }: Params) {
|
||||||
|
const links: (
|
||||||
| TWUI_LINK_LIST_LINK_OBJECT
|
| TWUI_LINK_LIST_LINK_OBJECT
|
||||||
| TWUI_LINK_LIST_LINK_OBJECT[]
|
| TWUI_LINK_LIST_LINK_OBJECT[]
|
||||||
| undefined
|
| undefined
|
||||||
)[] = [
|
)[] = [
|
||||||
{
|
{
|
||||||
title: "Dashboard",
|
title: "Dashboard",
|
||||||
url: "/admin",
|
url: "/admin",
|
||||||
@ -14,12 +18,17 @@ export const AdminAsideLinks: (
|
|||||||
title: "Services",
|
title: "Services",
|
||||||
url: "/admin/services",
|
url: "/admin/services",
|
||||||
},
|
},
|
||||||
{
|
user?.super_admin
|
||||||
|
? {
|
||||||
title: "Users",
|
title: "Users",
|
||||||
url: "/admin/users",
|
url: "/admin/users",
|
||||||
},
|
}
|
||||||
|
: undefined,
|
||||||
{
|
{
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
url: "/admin/settings",
|
url: "/admin/settings",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|||||||
@ -2,16 +2,18 @@ import LinkList from "@/twui/components/elements/LinkList";
|
|||||||
import Main from "@/twui/components/layout/Main";
|
import Main from "@/twui/components/layout/Main";
|
||||||
import Row from "@/twui/components/layout/Row";
|
import Row from "@/twui/components/layout/Row";
|
||||||
import Stack from "@/twui/components/layout/Stack";
|
import Stack from "@/twui/components/layout/Stack";
|
||||||
import { Fragment, PropsWithChildren } from "react";
|
import { Fragment, PropsWithChildren, useContext } from "react";
|
||||||
import { AdminAsideLinks } from "./(data)/links";
|
import { AdminAsideLinks } from "./(data)/links";
|
||||||
import Header from "./header";
|
import Header from "./header";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import Spacer from "@/twui/components/layout/Spacer";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import { AppContext } from "@/src/pages/_app";
|
||||||
|
|
||||||
type Props = PropsWithChildren & {};
|
type Props = PropsWithChildren & {};
|
||||||
|
|
||||||
export default function Layout({ children }: Props) {
|
export default function Layout({ children }: Props) {
|
||||||
|
const { pageProps } = useContext(AppContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Head>
|
<Head>
|
||||||
@ -26,7 +28,7 @@ export default function Layout({ children }: Props) {
|
|||||||
<Header />
|
<Header />
|
||||||
<Stack className="grid-cell col-span-6 xl:col-span-1 gap-0">
|
<Stack className="grid-cell col-span-6 xl:col-span-1 gap-0">
|
||||||
<LinkList
|
<LinkList
|
||||||
links={AdminAsideLinks}
|
links={AdminAsideLinks({ user: pageProps.user })}
|
||||||
className="w-full xl:flex-col"
|
className="w-full xl:flex-col"
|
||||||
linkProps={{
|
linkProps={{
|
||||||
className: "turboci-admin-aside-link",
|
className: "turboci-admin-aside-link",
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import "@/src/styles/globals.css";
|
import "@/src/styles/globals.css";
|
||||||
|
import "prism-themes/themes/prism-dracula.css";
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import { createContext } from "react";
|
import { createContext } from "react";
|
||||||
import { PagePropsType, TurboCIAdminAppContextType } from "../types";
|
import { PagePropsType, TurboCIAdminAppContextType } from "../types";
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import Main from "@/src/components/pages/admin/settings";
|
import Main from "@/src/components/pages/admin/settings";
|
||||||
|
import { NSQLITE_TURBOCI_ADMIN_USERS } from "@/src/db/types";
|
||||||
import defaultAdminProps from "@/src/functions/pages/admin/default-admin-props";
|
import defaultAdminProps from "@/src/functions/pages/admin/default-admin-props";
|
||||||
import Layout from "@/src/layouts/admin";
|
import Layout from "@/src/layouts/admin";
|
||||||
|
import NSQLite from "@moduletrace/nsqlite";
|
||||||
import { GetServerSideProps } from "next";
|
import { GetServerSideProps } from "next";
|
||||||
|
|
||||||
export default function AdminSettings() {
|
export default function AdminSettings() {
|
||||||
@ -12,5 +14,27 @@ export default function AdminSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||||
return await defaultAdminProps({ ctx });
|
return await defaultAdminProps({
|
||||||
|
ctx,
|
||||||
|
async propsFn({ user }) {
|
||||||
|
if (!user.id) {
|
||||||
|
return `/admin`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const users_res = await NSQLite.select<NSQLITE_TURBOCI_ADMIN_USERS>(
|
||||||
|
{
|
||||||
|
table: "users",
|
||||||
|
targetId: user.id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!users_res.singleRes?.id) {
|
||||||
|
return `/admin/users`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
deployment_user: users_res.singleRes,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,6 +29,10 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!users_res.singleRes?.id) {
|
||||||
|
return `/admin/users`;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
deployment_user: users_res.singleRes,
|
deployment_user: users_res.singleRes,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -73,7 +73,7 @@ export default async function handler(
|
|||||||
email,
|
email,
|
||||||
password: new_user_password,
|
password: new_user_password,
|
||||||
is_super_admin: user?.id ? 0 : 1,
|
is_super_admin: user?.id ? 0 : 1,
|
||||||
username: slugify(username),
|
username: username ? slugify(username) : undefined,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
table: "users",
|
table: "users",
|
||||||
@ -90,20 +90,31 @@ export default async function handler(
|
|||||||
(typeof NSQLiteTables)[number]
|
(typeof NSQLiteTables)[number]
|
||||||
>({
|
>({
|
||||||
table: "users",
|
table: "users",
|
||||||
query: {
|
targetId: new_user_id,
|
||||||
query: {
|
|
||||||
is_super_admin: { value: "1" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const newly_inserted_user = newly_inserted_user_res.singleRes;
|
const newly_inserted_user = newly_inserted_user_res.singleRes;
|
||||||
|
|
||||||
if (!newly_inserted_user?.id) {
|
if (!newly_inserted_user?.id) {
|
||||||
|
console.log("newly_inserted_user_res", newly_inserted_user_res);
|
||||||
throw new Error(`Couldn't Find Newly inserted user.`);
|
throw new Error(`Couldn't Find Newly inserted user.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user?.id && !newly_inserted_user.username) {
|
||||||
|
const newly_inserted_user_res = await NSQLite.delete<
|
||||||
|
NSQLITE_TURBOCI_ADMIN_USERS,
|
||||||
|
(typeof NSQLiteTables)[number]
|
||||||
|
>({
|
||||||
|
table: "users",
|
||||||
|
targetId: newly_inserted_user?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new Error(`Couldn't set Newly inserted user username.`);
|
||||||
|
}
|
||||||
|
|
||||||
if (user?.id) {
|
if (user?.id) {
|
||||||
|
console.log("newly_inserted_user", newly_inserted_user);
|
||||||
|
|
||||||
await setupDeploymentUser({ user_id: newly_inserted_user.id });
|
await setupDeploymentUser({ user_id: newly_inserted_user.id });
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user