wip
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
"@playwright/test": "1.28.1",
|
"@playwright/test": "1.28.1",
|
||||||
"@sveltejs/adapter-static": "1.0.0-next.48",
|
"@sveltejs/adapter-static": "1.0.0-next.48",
|
||||||
"@sveltejs/kit": "1.0.0-next.572",
|
"@sveltejs/kit": "1.0.0-next.572",
|
||||||
|
"@types/js-cookie": "3.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "5.44.0",
|
"@typescript-eslint/eslint-plugin": "5.44.0",
|
||||||
"@typescript-eslint/parser": "5.44.0",
|
"@typescript-eslint/parser": "5.44.0",
|
||||||
"autoprefixer": "10.4.13",
|
"autoprefixer": "10.4.13",
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { writable, readable, type Writable, type Readable } from 'svelte/store';
|
import { writable, readable, type Writable } from 'svelte/store';
|
||||||
import superjson from 'superjson';
|
import superjson from 'superjson';
|
||||||
import type { AppRouter, PrismaPermission } from 'server/src/trpc';
|
import type { AppRouter } from 'server/src/trpc';
|
||||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||||
import { browser, dev } from '$app/environment';
|
import { browser, dev } from '$app/environment';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
import { appSession } from '$lib/store';
|
import { appSession } from '$lib/store';
|
||||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import UpdateAvailable from '$lib/components/UpdateAvailable.svelte';
|
// import UpdateAvailable from '$lib/components/UpdateAvailable.svelte';
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import { errorNotification } from '$lib/common';
|
import { errorNotification } from '$lib/common';
|
||||||
import Toasts from '$lib/components/Toasts.svelte';
|
import Toasts from '$lib/components/Toasts.svelte';
|
||||||
@@ -346,7 +346,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
IAM {#if $appSession.pendingInvitations.length > 0}
|
IAM {#if $appSession.pendingInvitations.length > 0}
|
||||||
<span class="indicator-item rounded-full badge badge-primary"
|
<span class="indicator-item rounded-full badge badge-primary"
|
||||||
>{pendingInvitations.length}</span
|
>{$appSession.pendingInvitations.length}</span
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
|
@@ -2,6 +2,18 @@
|
|||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
|
import { dev } from '$app/environment';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
import { asyncSleep, errorNotification, getRndInteger } from '$lib/common';
|
||||||
|
import { appSession, search, t } from '$lib/store';
|
||||||
|
|
||||||
|
import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte';
|
||||||
|
import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte';
|
||||||
|
import ServiceIcons from '$lib/components/svg/services/ServiceIcons.svelte';
|
||||||
|
import NewResource from '$lib/components/NewResource.svelte';
|
||||||
|
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
applications,
|
applications,
|
||||||
foundUnconfiguredApplication,
|
foundUnconfiguredApplication,
|
||||||
@@ -14,17 +26,6 @@
|
|||||||
settings
|
settings
|
||||||
} = data;
|
} = data;
|
||||||
let filtered: any = setInitials();
|
let filtered: any = setInitials();
|
||||||
import { asyncSleep, errorNotification, getRndInteger } from '$lib/common';
|
|
||||||
import { appSession, search, t } from '$lib/store';
|
|
||||||
|
|
||||||
import ApplicationsIcons from '$lib/components/svg/applications/ApplicationIcons.svelte';
|
|
||||||
import DatabaseIcons from '$lib/components/svg/databases/DatabaseIcons.svelte';
|
|
||||||
import ServiceIcons from '$lib/components/svg/services/ServiceIcons.svelte';
|
|
||||||
import { dev } from '$app/environment';
|
|
||||||
import NewResource from '$lib/components/NewResource.svelte';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
|
||||||
|
|
||||||
let numberOfGetStatus = 0;
|
let numberOfGetStatus = 0;
|
||||||
let status: any = {};
|
let status: any = {};
|
||||||
let noInitialStatus: any = {
|
let noInitialStatus: any = {
|
||||||
@@ -155,7 +156,7 @@
|
|||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
let isDegraded = false;
|
let isDegraded = false;
|
||||||
if (buildPack || simpleDockerfile) {
|
if (buildPack || simpleDockerfile) {
|
||||||
const response = await t.applications.status.query({ id })
|
const response = await t.applications.status.query({ id });
|
||||||
if (response.length === 0) {
|
if (response.length === 0) {
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
} else if (response.length === 1) {
|
} else if (response.length === 1) {
|
||||||
@@ -177,7 +178,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (typeof dualCerts !== 'undefined') {
|
} else if (typeof dualCerts !== 'undefined') {
|
||||||
const response = await t.services.status.query({ id })
|
const response = await t.services.status.query({ id });
|
||||||
if (Object.keys(response).length === 0) {
|
if (Object.keys(response).length === 0) {
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
} else {
|
} else {
|
||||||
@@ -197,7 +198,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const response = await get(`/databases/${id}/status`);
|
const response = await t.databases.status.query({ id });
|
||||||
isRunning = response.isRunning;
|
isRunning = response.isRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +382,7 @@
|
|||||||
'Are you sure? This will delete all UNCONFIGURED applications and their data.'
|
'Are you sure? This will delete all UNCONFIGURED applications and their data.'
|
||||||
);
|
);
|
||||||
if (sure) {
|
if (sure) {
|
||||||
// await post(`/applications/cleanup/unconfigured`, {});
|
await t.applications.cleanup.query();
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -394,7 +395,7 @@
|
|||||||
'Are you sure? This will delete all UNCONFIGURED services and their data.'
|
'Are you sure? This will delete all UNCONFIGURED services and their data.'
|
||||||
);
|
);
|
||||||
if (sure) {
|
if (sure) {
|
||||||
// await post(`/services/cleanup/unconfigured`, {});
|
await t.services.cleanup.query();
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -407,7 +408,7 @@
|
|||||||
'Are you sure? This will delete all UNCONFIGURED databases and their data.'
|
'Are you sure? This will delete all UNCONFIGURED databases and their data.'
|
||||||
);
|
);
|
||||||
if (sure) {
|
if (sure) {
|
||||||
// await post(`/databases/cleanup/unconfigured`, {});
|
await t.databases.cleanup.query();
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -418,7 +419,7 @@
|
|||||||
try {
|
try {
|
||||||
const sure = confirm('Are you sure? This will delete this application!');
|
const sure = confirm('Are you sure? This will delete this application!');
|
||||||
if (sure) {
|
if (sure) {
|
||||||
// await del(`/applications/${id}`, { force: true });
|
await t.applications.delete.mutate({ id, force: true });
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -429,6 +430,7 @@
|
|||||||
try {
|
try {
|
||||||
const sure = confirm('Are you sure? This will delete this service!');
|
const sure = confirm('Are you sure? This will delete this service!');
|
||||||
if (sure) {
|
if (sure) {
|
||||||
|
await t.services.delete.mutate({ id });
|
||||||
// await del(`/services/${id}`, {});
|
// await del(`/services/${id}`, {});
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
}
|
}
|
||||||
@@ -440,7 +442,7 @@
|
|||||||
try {
|
try {
|
||||||
const sure = confirm('Are you sure? This will delete this database!');
|
const sure = confirm('Are you sure? This will delete this database!');
|
||||||
if (sure) {
|
if (sure) {
|
||||||
// await del(`/databases/${id}`, { force: true });
|
await t.databases.delete.mutate({ id, force: true });
|
||||||
return window.location.reload();
|
return window.location.reload();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"extends": "./.svelte-kit/tsconfig.json",
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"exclude": ["node_modules/*", ".svelte-kit/*", "public/*"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
@@ -10,12 +11,8 @@
|
|||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"paths": {
|
"paths": {
|
||||||
"$lib": [
|
"$lib": ["src/lib"],
|
||||||
"src/lib"
|
"$lib/*": ["src/lib/*"]
|
||||||
],
|
|
||||||
"$lib/*": [
|
|
||||||
"src/lib/*"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -51,6 +51,7 @@
|
|||||||
"@types/node": "18.11.9",
|
"@types/node": "18.11.9",
|
||||||
"@types/node-fetch": "2.6.2",
|
"@types/node-fetch": "2.6.2",
|
||||||
"@types/shell-quote": "^1.7.1",
|
"@types/shell-quote": "^1.7.1",
|
||||||
|
"@types/bcryptjs": "^2.4.2",
|
||||||
"@types/ws": "8.5.3",
|
"@types/ws": "8.5.3",
|
||||||
"npm-run-all": "4.1.5",
|
"npm-run-all": "4.1.5",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv');
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
// const isDev = process.env.NODE_ENV === 'development';
|
||||||
// dotenv.config({ path: isDev ? '../../.env' : '.env' });
|
// dotenv.config({ path: isDev ? '../../.env' : '.env' });
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
const { z } = require('zod');
|
const { z } = require('zod');
|
||||||
|
@@ -68,8 +68,8 @@ export const decrypt = (hashString: string) => {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function generateRangeArray(start, end) {
|
export function generateRangeArray(start: number, end: number) {
|
||||||
return Array.from({ length: end - start }, (v, k) => k + start);
|
return Array.from({ length: end - start }, (_v, k) => k + start);
|
||||||
}
|
}
|
||||||
export function generateTimestamp(): string {
|
export function generateTimestamp(): string {
|
||||||
return `${day().format('HH:mm:ss.SSS')}`;
|
return `${day().format('HH:mm:ss.SSS')}`;
|
||||||
@@ -94,7 +94,7 @@ export async function getTemplates() {
|
|||||||
let data = await open.readFile({ encoding: 'utf-8' });
|
let data = await open.readFile({ encoding: 'utf-8' });
|
||||||
let jsonData = JSON.parse(data);
|
let jsonData = JSON.parse(data);
|
||||||
if (isARM(process.arch)) {
|
if (isARM(process.arch)) {
|
||||||
jsonData = jsonData.filter((d) => d.arch !== 'amd64');
|
jsonData = jsonData.filter((d: { arch: string }) => d.arch !== 'amd64');
|
||||||
}
|
}
|
||||||
return jsonData;
|
return jsonData;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -109,3 +109,26 @@ export function isARM(arch: string) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeService({ id }: { id: string }): Promise<void> {
|
||||||
|
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.serviceSetting.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.ghost.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.umami.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.hasura.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.minio.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.vscodeserver.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.wordpress.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.moodle.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.searxng.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.weblate.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.taiga.deleteMany({ where: { serviceId: id } });
|
||||||
|
|
||||||
|
await prisma.service.delete({ where: { id } });
|
||||||
|
}
|
||||||
|
@@ -1,31 +1,39 @@
|
|||||||
import { executeCommand } from "./executeCommand";
|
import { executeCommand } from './executeCommand';
|
||||||
|
|
||||||
export async function checkContainer({ dockerId, container, remove = false }: { dockerId: string, container: string, remove?: boolean }): Promise<{ found: boolean, status?: { isExited: boolean, isRunning: boolean, isRestarting: boolean } }> {
|
export async function checkContainer({
|
||||||
|
dockerId,
|
||||||
|
container,
|
||||||
|
remove = false
|
||||||
|
}: {
|
||||||
|
dockerId: string;
|
||||||
|
container: string;
|
||||||
|
remove?: boolean;
|
||||||
|
}): Promise<{
|
||||||
|
found: boolean;
|
||||||
|
status?: { isExited: boolean; isRunning: boolean; isRestarting: boolean };
|
||||||
|
}> {
|
||||||
let containerFound = false;
|
let containerFound = false;
|
||||||
try {
|
try {
|
||||||
const { stdout } = await executeCommand({
|
const { stdout } = await executeCommand({
|
||||||
dockerId,
|
dockerId,
|
||||||
command:
|
command: `docker inspect --format '{{json .State}}' ${container}`
|
||||||
`docker inspect --format '{{json .State}}' ${container}`
|
|
||||||
});
|
});
|
||||||
containerFound = true
|
containerFound = true;
|
||||||
const parsedStdout = JSON.parse(stdout);
|
const parsedStdout = JSON.parse(stdout);
|
||||||
const status = parsedStdout.Status;
|
const status = parsedStdout.Status;
|
||||||
const isRunning = status === 'running';
|
const isRunning = status === 'running';
|
||||||
const isRestarting = status === 'restarting'
|
const isRestarting = status === 'restarting';
|
||||||
const isExited = status === 'exited'
|
const isExited = status === 'exited';
|
||||||
if (status === 'created') {
|
if (status === 'created') {
|
||||||
await executeCommand({
|
await executeCommand({
|
||||||
dockerId,
|
dockerId,
|
||||||
command:
|
command: `docker rm ${container}`
|
||||||
`docker rm ${container}`
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (remove && status === 'exited') {
|
if (remove && status === 'exited') {
|
||||||
await executeCommand({
|
await executeCommand({
|
||||||
dockerId,
|
dockerId,
|
||||||
command:
|
command: `docker rm ${container}`
|
||||||
`docker rm ${container}`
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,5 +51,74 @@ export async function checkContainer({ dockerId, container, remove = false }: {
|
|||||||
return {
|
return {
|
||||||
found: false
|
found: false
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeContainer({
|
||||||
|
id,
|
||||||
|
dockerId
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
dockerId: string;
|
||||||
|
}): Promise<void> {
|
||||||
|
try {
|
||||||
|
const { stdout } = await executeCommand({
|
||||||
|
dockerId,
|
||||||
|
command: `docker inspect --format '{{json .State}}' ${id}`
|
||||||
|
});
|
||||||
|
if (JSON.parse(stdout).Running) {
|
||||||
|
await executeCommand({ dockerId, command: `docker stop -t 0 ${id}` });
|
||||||
|
await executeCommand({ dockerId, command: `docker rm ${id}` });
|
||||||
|
}
|
||||||
|
if (JSON.parse(stdout).Status === 'exited') {
|
||||||
|
await executeCommand({ dockerId, command: `docker rm ${id}` });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stopDatabaseContainer(database: any): Promise<boolean> {
|
||||||
|
let everStarted = false;
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
destinationDockerId,
|
||||||
|
destinationDocker: { engine, id: dockerId }
|
||||||
|
} = database;
|
||||||
|
if (destinationDockerId) {
|
||||||
|
try {
|
||||||
|
const { stdout } = await executeCommand({
|
||||||
|
dockerId,
|
||||||
|
command: `docker inspect --format '{{json .State}}' ${id}`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
everStarted = true;
|
||||||
|
await removeContainer({ id, dockerId });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return everStarted;
|
||||||
|
}
|
||||||
|
export async function stopTcpHttpProxy(
|
||||||
|
id: string,
|
||||||
|
destinationDocker: any,
|
||||||
|
publicPort: number,
|
||||||
|
forceName: string | null = null
|
||||||
|
): Promise<{ stdout: string; stderr: string } | Error | unknown> {
|
||||||
|
const { id: dockerId } = destinationDocker;
|
||||||
|
let container = `${id}-${publicPort}`;
|
||||||
|
if (forceName) container = forceName;
|
||||||
|
const { found } = await checkContainer({ dockerId, container });
|
||||||
|
try {
|
||||||
|
if (!found) return true;
|
||||||
|
return await executeCommand({
|
||||||
|
dockerId,
|
||||||
|
command: `docker stop -t 0 ${container} && docker rm ${container}`,
|
||||||
|
shell: true
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -6,7 +6,7 @@ import sshConfig from 'ssh-config';
|
|||||||
|
|
||||||
import { getFreeSSHLocalPort } from './ssh';
|
import { getFreeSSHLocalPort } from './ssh';
|
||||||
import { env } from '../env';
|
import { env } from '../env';
|
||||||
import { saveBuildLog } from './logging';
|
import { BuildLog, saveBuildLog } from './logging';
|
||||||
import { decrypt } from './common';
|
import { decrypt } from './common';
|
||||||
|
|
||||||
export async function executeCommand({
|
export async function executeCommand({
|
||||||
@@ -31,23 +31,26 @@ export async function executeCommand({
|
|||||||
const { execa, execaCommand } = await import('execa');
|
const { execa, execaCommand } = await import('execa');
|
||||||
const { parse } = await import('shell-quote');
|
const { parse } = await import('shell-quote');
|
||||||
const parsedCommand = parse(command);
|
const parsedCommand = parse(command);
|
||||||
const dockerCommand = parsedCommand[0];
|
const dockerCommand = parsedCommand[0]?.toString();
|
||||||
const dockerArgs = parsedCommand.slice(1);
|
const dockerArgs = parsedCommand.slice(1).toString();
|
||||||
|
|
||||||
if (dockerId) {
|
if (dockerId && dockerCommand && dockerArgs) {
|
||||||
const destinationDocker = await prisma.destinationDocker.findUnique({
|
const destinationDocker = await prisma.destinationDocker.findUnique({
|
||||||
where: { id: dockerId }
|
where: { id: dockerId }
|
||||||
});
|
});
|
||||||
if (!destinationDocker) {
|
if (!destinationDocker) {
|
||||||
throw new Error('Destination docker not found');
|
throw new Error('Destination docker not found');
|
||||||
}
|
}
|
||||||
let { remoteEngine, remoteIpAddress, engine } = destinationDocker;
|
let {
|
||||||
|
remoteEngine,
|
||||||
|
remoteIpAddress,
|
||||||
|
engine = 'unix:///var/run/docker.sock'
|
||||||
|
} = destinationDocker;
|
||||||
if (remoteEngine) {
|
if (remoteEngine) {
|
||||||
await createRemoteEngineConfiguration(dockerId);
|
await createRemoteEngineConfiguration(dockerId);
|
||||||
engine = `ssh://${remoteIpAddress}-remote`;
|
engine = `ssh://${remoteIpAddress}-remote`;
|
||||||
} else {
|
|
||||||
engine = 'unix:///var/run/docker.sock';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (env.CODESANDBOX_HOST) {
|
if (env.CODESANDBOX_HOST) {
|
||||||
if (command.startsWith('docker compose')) {
|
if (command.startsWith('docker compose')) {
|
||||||
command = command.replace(/docker compose/gi, 'docker-compose');
|
command = command.replace(/docker compose/gi, 'docker-compose');
|
||||||
@@ -73,12 +76,12 @@ export async function executeCommand({
|
|||||||
}
|
}
|
||||||
const logs: any[] = [];
|
const logs: any[] = [];
|
||||||
if (subprocess && subprocess.stdout && subprocess.stderr) {
|
if (subprocess && subprocess.stdout && subprocess.stderr) {
|
||||||
subprocess.stdout.on('data', async (data) => {
|
subprocess.stdout.on('data', async (data: string) => {
|
||||||
const stdout = data.toString();
|
const stdout = data.toString();
|
||||||
const array = stdout.split('\n');
|
const array = stdout.split('\n');
|
||||||
for (const line of array) {
|
for (const line of array) {
|
||||||
if (line !== '\n' && line !== '') {
|
if (line !== '\n' && line !== '') {
|
||||||
const log = {
|
const log: BuildLog = {
|
||||||
line: `${line.replace('\n', '')}`,
|
line: `${line.replace('\n', '')}`,
|
||||||
buildId,
|
buildId,
|
||||||
applicationId
|
applicationId
|
||||||
@@ -90,7 +93,7 @@ export async function executeCommand({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
subprocess.stderr.on('data', async (data) => {
|
subprocess.stderr.on('data', async (data: string) => {
|
||||||
const stderr = data.toString();
|
const stderr = data.toString();
|
||||||
const array = stderr.split('\n');
|
const array = stderr.split('\n');
|
||||||
for (const line of array) {
|
for (const line of array) {
|
||||||
@@ -107,7 +110,7 @@ export async function executeCommand({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
subprocess.on('exit', async (code) => {
|
subprocess.on('exit', async (code: number) => {
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
resolve('success');
|
resolve('success');
|
||||||
} else {
|
} else {
|
||||||
|
@@ -2,15 +2,13 @@ import { prisma } from '../prisma';
|
|||||||
import { encrypt, generateTimestamp, isDev } from './common';
|
import { encrypt, generateTimestamp, isDev } from './common';
|
||||||
import { day } from './dayjs';
|
import { day } from './dayjs';
|
||||||
|
|
||||||
export const saveBuildLog = async ({
|
export type Line = string | { shortMessage: string; stderr: string };
|
||||||
line,
|
export type BuildLog = {
|
||||||
buildId,
|
line: Line;
|
||||||
applicationId
|
buildId?: string;
|
||||||
}: {
|
applicationId?: string;
|
||||||
line: string;
|
};
|
||||||
buildId: string;
|
export const saveBuildLog = async ({ line, buildId, applicationId }: BuildLog): Promise<any> => {
|
||||||
applicationId: string;
|
|
||||||
}): Promise<any> => {
|
|
||||||
if (buildId === 'undefined' || buildId === 'null' || !buildId) return;
|
if (buildId === 'undefined' || buildId === 'null' || !buildId) return;
|
||||||
if (applicationId === 'undefined' || applicationId === 'null' || !applicationId) return;
|
if (applicationId === 'undefined' || applicationId === 'null' || !applicationId) return;
|
||||||
const { default: got } = await import('got');
|
const { default: got } = await import('got');
|
||||||
|
@@ -12,7 +12,7 @@ const prismaGlobal = global as typeof global & {
|
|||||||
export const prisma: PrismaClient =
|
export const prisma: PrismaClient =
|
||||||
prismaGlobal.prisma ||
|
prismaGlobal.prisma ||
|
||||||
new PrismaClient({
|
new PrismaClient({
|
||||||
log: env.NODE_ENV === 'developments' ? ['query', 'error', 'warn'] : ['error']
|
log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error']
|
||||||
});
|
});
|
||||||
|
|
||||||
if (env.NODE_ENV !== 'production') {
|
if (env.NODE_ENV !== 'production') {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { inferAsyncReturnType } from '@trpc/server';
|
import type { inferAsyncReturnType } from '@trpc/server';
|
||||||
import { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';
|
import type { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import { env } from '../env';
|
import { env } from '../env';
|
||||||
export interface User {
|
export interface User {
|
||||||
|
@@ -6,7 +6,8 @@ import {
|
|||||||
authRouter,
|
authRouter,
|
||||||
dashboardRouter,
|
dashboardRouter,
|
||||||
applicationsRouter,
|
applicationsRouter,
|
||||||
servicesRouter
|
servicesRouter,
|
||||||
|
databasesRouter
|
||||||
} from './routers';
|
} from './routers';
|
||||||
|
|
||||||
export const appRouter = router({
|
export const appRouter = router({
|
||||||
@@ -14,7 +15,8 @@ export const appRouter = router({
|
|||||||
auth: authRouter,
|
auth: authRouter,
|
||||||
dashboard: dashboardRouter,
|
dashboard: dashboardRouter,
|
||||||
applications: applicationsRouter,
|
applications: applicationsRouter,
|
||||||
services: servicesRouter
|
services: servicesRouter,
|
||||||
|
databases: databasesRouter
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
@@ -1,19 +1,13 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { privateProcedure, router } from '../trpc';
|
import { privateProcedure, router } from '../trpc';
|
||||||
import { decrypt, isARM, listSettings } from '../../lib/common';
|
import { decrypt, isARM } from '../../lib/common';
|
||||||
import { prisma } from '../../prisma';
|
import { prisma } from '../../prisma';
|
||||||
import { executeCommand } from '../../lib/executeCommand';
|
import { executeCommand } from '../../lib/executeCommand';
|
||||||
import { checkContainer } from '../../lib/docker';
|
import { checkContainer, removeContainer } from '../../lib/docker';
|
||||||
|
|
||||||
export const applicationsRouter = router({
|
export const applicationsRouter = router({
|
||||||
status: privateProcedure
|
status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
|
||||||
.input(
|
const id: string = input.id;
|
||||||
z.object({
|
|
||||||
id: z.string()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.query(async ({ ctx, input }) => {
|
|
||||||
const id = input.id;
|
|
||||||
const teamId = ctx.user?.teamId;
|
const teamId = ctx.user?.teamId;
|
||||||
if (!teamId) {
|
if (!teamId) {
|
||||||
throw { status: 400, message: 'Team not found.' };
|
throw { status: 400, message: 'Team not found.' };
|
||||||
@@ -77,6 +71,84 @@ export const applicationsRouter = router({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return payload;
|
return payload;
|
||||||
|
}),
|
||||||
|
cleanup: privateProcedure.query(async ({ ctx }) => {
|
||||||
|
const teamId = ctx.user?.teamId;
|
||||||
|
let applications = await prisma.application.findMany({
|
||||||
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { settings: true, destinationDocker: true, teams: true }
|
||||||
|
});
|
||||||
|
for (const application of applications) {
|
||||||
|
if (
|
||||||
|
!application.buildPack ||
|
||||||
|
!application.destinationDockerId ||
|
||||||
|
!application.branch ||
|
||||||
|
(!application.settings?.isBot && !application?.fqdn)
|
||||||
|
) {
|
||||||
|
if (application?.destinationDockerId && application.destinationDocker?.network) {
|
||||||
|
const { stdout: containers } = await executeCommand({
|
||||||
|
dockerId: application.destinationDocker.id,
|
||||||
|
command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${application.id} --format '{{json .}}'`
|
||||||
|
});
|
||||||
|
if (containers) {
|
||||||
|
const containersArray = containers.trim().split('\n');
|
||||||
|
for (const container of containersArray) {
|
||||||
|
const containerObj = JSON.parse(container);
|
||||||
|
const id = containerObj.ID;
|
||||||
|
await removeContainer({ id, dockerId: application.destinationDocker.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await prisma.applicationSettings.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.buildLog.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.build.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.secret.deleteMany({ where: { applicationId: application.id } });
|
||||||
|
await prisma.applicationPersistentStorage.deleteMany({
|
||||||
|
where: { applicationId: application.id }
|
||||||
|
});
|
||||||
|
await prisma.applicationConnectedDatabase.deleteMany({
|
||||||
|
where: { applicationId: application.id }
|
||||||
|
});
|
||||||
|
await prisma.application.deleteMany({ where: { id: application.id } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
|
delete: privateProcedure
|
||||||
|
.input(z.object({ force: z.boolean(), id: z.string() }))
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const { id, force } = input;
|
||||||
|
const teamId = ctx.user?.teamId;
|
||||||
|
const application = await prisma.application.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: { destinationDocker: true }
|
||||||
|
});
|
||||||
|
if (!force && application?.destinationDockerId && application.destinationDocker?.network) {
|
||||||
|
const { stdout: containers } = await executeCommand({
|
||||||
|
dockerId: application.destinationDocker.id,
|
||||||
|
command: `docker ps -a --filter network=${application.destinationDocker.network} --filter name=${id} --format '{{json .}}'`
|
||||||
|
});
|
||||||
|
if (containers) {
|
||||||
|
const containersArray = containers.trim().split('\n');
|
||||||
|
for (const container of containersArray) {
|
||||||
|
const containerObj = JSON.parse(container);
|
||||||
|
const id = containerObj.ID;
|
||||||
|
await removeContainer({ id, dockerId: application.destinationDocker.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await prisma.applicationSettings.deleteMany({ where: { application: { id } } });
|
||||||
|
await prisma.buildLog.deleteMany({ where: { applicationId: id } });
|
||||||
|
await prisma.build.deleteMany({ where: { applicationId: id } });
|
||||||
|
await prisma.secret.deleteMany({ where: { applicationId: id } });
|
||||||
|
await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } });
|
||||||
|
await prisma.applicationConnectedDatabase.deleteMany({ where: { applicationId: id } });
|
||||||
|
if (teamId === '0') {
|
||||||
|
await prisma.application.deleteMany({ where: { id } });
|
||||||
|
} else {
|
||||||
|
await prisma.application.deleteMany({ where: { id, teams: { some: { id: teamId } } } });
|
||||||
|
}
|
||||||
|
return {};
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
84
apps/server/src/trpc/routers/databases.ts
Normal file
84
apps/server/src/trpc/routers/databases.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
import { privateProcedure, router } from '../trpc';
|
||||||
|
import { decrypt } from '../../lib/common';
|
||||||
|
import { prisma } from '../../prisma';
|
||||||
|
import { executeCommand } from '../../lib/executeCommand';
|
||||||
|
import { stopDatabaseContainer, stopTcpHttpProxy } from '../../lib/docker';
|
||||||
|
|
||||||
|
export const databasesRouter = router({
|
||||||
|
status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
|
||||||
|
const id = input.id;
|
||||||
|
const teamId = ctx.user?.teamId;
|
||||||
|
|
||||||
|
let isRunning = false;
|
||||||
|
const database = await prisma.database.findFirst({
|
||||||
|
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { destinationDocker: true, settings: true }
|
||||||
|
});
|
||||||
|
if (database) {
|
||||||
|
const { destinationDockerId, destinationDocker } = database;
|
||||||
|
if (destinationDockerId) {
|
||||||
|
try {
|
||||||
|
const { stdout } = await executeCommand({
|
||||||
|
dockerId: destinationDocker.id,
|
||||||
|
command: `docker inspect --format '{{json .State}}' ${id}`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (JSON.parse(stdout).Running) {
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
isRunning
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
cleanup: privateProcedure.query(async ({ ctx }) => {
|
||||||
|
const teamId = ctx.user?.teamId;
|
||||||
|
let databases = await prisma.database.findMany({
|
||||||
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { settings: true, destinationDocker: true, teams: true }
|
||||||
|
});
|
||||||
|
for (const database of databases) {
|
||||||
|
if (!database?.version) {
|
||||||
|
const { id } = database;
|
||||||
|
if (database.destinationDockerId) {
|
||||||
|
const everStarted = await stopDatabaseContainer(database);
|
||||||
|
if (everStarted)
|
||||||
|
await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||||
|
}
|
||||||
|
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
|
||||||
|
await prisma.databaseSecret.deleteMany({ where: { databaseId: id } });
|
||||||
|
await prisma.database.delete({ where: { id } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}),
|
||||||
|
delete: privateProcedure
|
||||||
|
.input(z.object({ id: z.string(), force: z.boolean() }))
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const { id, force } = input;
|
||||||
|
const teamId = ctx.user?.teamId;
|
||||||
|
const database = await prisma.database.findFirst({
|
||||||
|
where: { id, teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { destinationDocker: true, settings: true }
|
||||||
|
});
|
||||||
|
if (!force) {
|
||||||
|
if (database.dbUserPassword) database.dbUserPassword = decrypt(database.dbUserPassword);
|
||||||
|
if (database.rootUserPassword)
|
||||||
|
database.rootUserPassword = decrypt(database.rootUserPassword);
|
||||||
|
if (database.destinationDockerId) {
|
||||||
|
const everStarted = await stopDatabaseContainer(database);
|
||||||
|
if (everStarted)
|
||||||
|
await stopTcpHttpProxy(id, database.destinationDocker, database.publicPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await prisma.databaseSettings.deleteMany({ where: { databaseId: id } });
|
||||||
|
await prisma.databaseSecret.deleteMany({ where: { databaseId: id } });
|
||||||
|
await prisma.database.delete({ where: { id } });
|
||||||
|
return {};
|
||||||
|
})
|
||||||
|
});
|
@@ -3,3 +3,4 @@ export * from './dashboard';
|
|||||||
export * from './settings';
|
export * from './settings';
|
||||||
export * from './applications';
|
export * from './applications';
|
||||||
export * from './services';
|
export * from './services';
|
||||||
|
export * from './databases';
|
||||||
|
@@ -1,18 +1,11 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { privateProcedure, router } from '../trpc';
|
import { privateProcedure, router } from '../trpc';
|
||||||
import { decrypt, getTemplates, listSettings } from '../../lib/common';
|
import { decrypt, getTemplates, removeService } from '../../lib/common';
|
||||||
import { prisma } from '../../prisma';
|
import { prisma } from '../../prisma';
|
||||||
import { executeCommand } from '../../lib/executeCommand';
|
import { executeCommand } from '../../lib/executeCommand';
|
||||||
import { checkContainer } from '../../lib/docker';
|
|
||||||
|
|
||||||
export const servicesRouter = router({
|
export const servicesRouter = router({
|
||||||
status: privateProcedure
|
status: privateProcedure.input(z.object({ id: z.string() })).query(async ({ ctx, input }) => {
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
id: z.string()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.query(async ({ ctx, input }) => {
|
|
||||||
const id = input.id;
|
const id = input.id;
|
||||||
const teamId = ctx.user?.teamId;
|
const teamId = ctx.user?.teamId;
|
||||||
if (!teamId) {
|
if (!teamId) {
|
||||||
@@ -30,7 +23,7 @@ export const servicesRouter = router({
|
|||||||
const containersArray = containers.trim().split('\n');
|
const containersArray = containers.trim().split('\n');
|
||||||
if (containersArray.length > 0 && containersArray[0] !== '') {
|
if (containersArray.length > 0 && containersArray[0] !== '') {
|
||||||
const templates = await getTemplates();
|
const templates = await getTemplates();
|
||||||
let template = templates.find((t) => t.type === service.type);
|
let template = templates.find((t: { type: string }) => t.type === service.type);
|
||||||
const templateStr = JSON.stringify(template);
|
const templateStr = JSON.stringify(template);
|
||||||
if (templateStr) {
|
if (templateStr) {
|
||||||
template = JSON.parse(templateStr.replaceAll('$$id', service.id));
|
template = JSON.parse(templateStr.replaceAll('$$id', service.id));
|
||||||
@@ -77,6 +70,66 @@ export const servicesRouter = router({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return payload;
|
return payload;
|
||||||
|
}),
|
||||||
|
cleanup: privateProcedure.query(async ({ ctx }) => {
|
||||||
|
const teamId = ctx.user?.teamId;
|
||||||
|
let services = await prisma.service.findMany({
|
||||||
|
where: { teams: { some: { id: teamId === '0' ? undefined : teamId } } },
|
||||||
|
include: { destinationDocker: true, teams: true }
|
||||||
|
});
|
||||||
|
for (const service of services) {
|
||||||
|
if (!service.fqdn) {
|
||||||
|
if (service.destinationDockerId) {
|
||||||
|
const { stdout: containers } = await executeCommand({
|
||||||
|
dockerId: service.destinationDockerId,
|
||||||
|
command: `docker ps -a --filter 'label=com.docker.compose.project=${service.id}' --format {{.ID}}`
|
||||||
|
});
|
||||||
|
if (containers) {
|
||||||
|
const containerArray = containers.split('\n');
|
||||||
|
if (containerArray.length > 0) {
|
||||||
|
for (const container of containerArray) {
|
||||||
|
await executeCommand({
|
||||||
|
dockerId: service.destinationDockerId,
|
||||||
|
command: `docker stop -t 0 ${container}`
|
||||||
|
});
|
||||||
|
await executeCommand({
|
||||||
|
dockerId: service.destinationDockerId,
|
||||||
|
command: `docker rm --force ${container}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await removeService({ id: service.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
delete: privateProcedure
|
||||||
|
.input(z.object({ force: z.boolean(), id: z.string() }))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
// todo: check if user is allowed to delete service
|
||||||
|
const { id } = input;
|
||||||
|
await prisma.serviceSecret.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.serviceSetting.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.servicePersistentStorage.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.meiliSearch.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.fider.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.ghost.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.umami.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.hasura.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.plausibleAnalytics.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.minio.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.vscodeserver.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.wordpress.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.glitchTip.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.moodle.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.appwrite.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.searxng.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.weblate.deleteMany({ where: { serviceId: id } });
|
||||||
|
await prisma.taiga.deleteMany({ where: { serviceId: id } });
|
||||||
|
|
||||||
|
await prisma.service.delete({ where: { id } });
|
||||||
|
return {};
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -160,6 +160,7 @@ importers:
|
|||||||
'@sveltejs/kit': 1.0.0-next.572
|
'@sveltejs/kit': 1.0.0-next.572
|
||||||
'@trpc/client': 10.1.0
|
'@trpc/client': 10.1.0
|
||||||
'@trpc/server': 10.1.0
|
'@trpc/server': 10.1.0
|
||||||
|
'@types/js-cookie': 3.0.2
|
||||||
'@typescript-eslint/eslint-plugin': 5.44.0
|
'@typescript-eslint/eslint-plugin': 5.44.0
|
||||||
'@typescript-eslint/parser': 5.44.0
|
'@typescript-eslint/parser': 5.44.0
|
||||||
autoprefixer: 10.4.13
|
autoprefixer: 10.4.13
|
||||||
@@ -196,6 +197,7 @@ importers:
|
|||||||
'@playwright/test': 1.28.1
|
'@playwright/test': 1.28.1
|
||||||
'@sveltejs/adapter-static': 1.0.0-next.48
|
'@sveltejs/adapter-static': 1.0.0-next.48
|
||||||
'@sveltejs/kit': 1.0.0-next.572_svelte@3.53.1+vite@3.2.4
|
'@sveltejs/kit': 1.0.0-next.572_svelte@3.53.1+vite@3.2.4
|
||||||
|
'@types/js-cookie': 3.0.2
|
||||||
'@typescript-eslint/eslint-plugin': 5.44.0_fnsv2sbzcckq65bwfk7a5xwslu
|
'@typescript-eslint/eslint-plugin': 5.44.0_fnsv2sbzcckq65bwfk7a5xwslu
|
||||||
'@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a
|
'@typescript-eslint/parser': 5.44.0_hsf322ms6xhhd4b5ne6lb74y4a
|
||||||
autoprefixer: 10.4.13_postcss@8.4.19
|
autoprefixer: 10.4.13_postcss@8.4.19
|
||||||
@@ -237,6 +239,7 @@ importers:
|
|||||||
'@prisma/client': 4.6.1
|
'@prisma/client': 4.6.1
|
||||||
'@trpc/client': 10.1.0
|
'@trpc/client': 10.1.0
|
||||||
'@trpc/server': 10.1.0
|
'@trpc/server': 10.1.0
|
||||||
|
'@types/bcryptjs': ^2.4.2
|
||||||
'@types/jsonwebtoken': ^8.5.9
|
'@types/jsonwebtoken': ^8.5.9
|
||||||
'@types/node': 18.11.9
|
'@types/node': 18.11.9
|
||||||
'@types/node-fetch': 2.6.2
|
'@types/node-fetch': 2.6.2
|
||||||
@@ -299,6 +302,7 @@ importers:
|
|||||||
ws: 8.11.0
|
ws: 8.11.0
|
||||||
zod: 3.19.1
|
zod: 3.19.1
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/bcryptjs': 2.4.2
|
||||||
'@types/jsonwebtoken': 8.5.9
|
'@types/jsonwebtoken': 8.5.9
|
||||||
'@types/node': 18.11.9
|
'@types/node': 18.11.9
|
||||||
'@types/node-fetch': 2.6.2
|
'@types/node-fetch': 2.6.2
|
||||||
@@ -2026,6 +2030,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==}
|
resolution: {integrity: sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/bcryptjs/2.4.2:
|
||||||
|
resolution: {integrity: sha512-LiMQ6EOPob/4yUL66SZzu6Yh77cbzJFYll+ZfaPiPPFswtIlA/Fs1MzdKYA7JApHU49zQTbJGX3PDmCpIdDBRQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/cacheable-request/6.0.2:
|
/@types/cacheable-request/6.0.2:
|
||||||
resolution: {integrity: sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==}
|
resolution: {integrity: sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Reference in New Issue
Block a user