mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Ready for 1.1.5-rc.1
This commit is contained in:
@@ -18,9 +18,14 @@ export const metadata: Metadata = {
|
|||||||
export default async function RoutePage(props: PageParams<{}>) {
|
export default async function RoutePage(props: PageParams<{}>) {
|
||||||
|
|
||||||
const agents = await db.query.agent.findMany({
|
const agents = await db.query.agent.findMany({
|
||||||
where: not(eq(drizzleDb.schemas.agent.isArchived, true))
|
where: not(eq(drizzleDb.schemas.agent.isArchived, true)),
|
||||||
|
with: {
|
||||||
|
databases: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log(agents);
|
||||||
|
|
||||||
|
|
||||||
if (!agents) {
|
if (!agents) {
|
||||||
notFound();
|
notFound();
|
||||||
|
|||||||
+9
-8
@@ -1,12 +1,10 @@
|
|||||||
import {PageParams} from "@/types/next";
|
import {PageParams} from "@/types/next";
|
||||||
import {notFound, redirect} from "next/navigation";
|
import {notFound, redirect} from "next/navigation";
|
||||||
import {Page, PageActions, PageContent, PageDescription, PageTitle} from "@/features/layout/page";
|
import {Page, PageContent, PageDescription, PageTitle} from "@/features/layout/page";
|
||||||
import {BackupButton} from "@/components/wrappers/dashboard/backup/backup-button/backup-button";
|
import {BackupButton} from "@/components/wrappers/dashboard/backup/backup-button/backup-button";
|
||||||
import {DatabaseTabs} from "@/components/wrappers/dashboard/projects/database/database-tabs";
|
import {DatabaseTabs} from "@/components/wrappers/dashboard/projects/database/database-tabs";
|
||||||
import {DatabaseKpi} from "@/components/wrappers/dashboard/projects/database/database-kpi";
|
import {DatabaseKpi} from "@/components/wrappers/dashboard/projects/database/database-kpi";
|
||||||
import {EditButton} from "@/components/wrappers/dashboard/database/edit-button/edit-button";
|
|
||||||
import {CronButton} from "@/components/wrappers/dashboard/database/cron-button/cron-button";
|
import {CronButton} from "@/components/wrappers/dashboard/database/cron-button/cron-button";
|
||||||
|
|
||||||
import {db} from "@/db";
|
import {db} from "@/db";
|
||||||
import {eq, and, inArray} from "drizzle-orm";
|
import {eq, and, inArray} from "drizzle-orm";
|
||||||
import * as drizzleDb from "@/db";
|
import * as drizzleDb from "@/db";
|
||||||
@@ -92,22 +90,23 @@ export default async function RoutePage(props: PageParams<{
|
|||||||
|
|
||||||
const isMember = activeMember?.role === "member";
|
const isMember = activeMember?.role === "member";
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
|
|
||||||
<div className="justify-between gap-2 sm:flex">
|
<div className="justify-between gap-2 sm:flex">
|
||||||
<PageTitle className="flex flex-col md:flex-row items-center justify-between w-full">
|
<PageTitle className="flex flex-col md:flex-row items-center justify-between w-full ">
|
||||||
<div className=" w-full md:w-fit">
|
<div className="min-w-full md:min-w-fit ">
|
||||||
{capitalizeFirstLetter(dbItem.name)}
|
{capitalizeFirstLetter(dbItem.name)}
|
||||||
</div>
|
</div>
|
||||||
{!isMember && (
|
{!isMember && (
|
||||||
<div className="flex items-center gap-2 md:justify-between w-full">
|
<div className="flex items-center gap-2 md:justify-between w-full ">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{/* Do not delete*/}
|
{/* Do not delete*/}
|
||||||
{/*<EditButton/>*/}
|
{/*<EditButton/>*/}
|
||||||
<RetentionPolicySheet database={dbItem}/>
|
<RetentionPolicySheet database={dbItem}/>
|
||||||
<CronButton database={dbItem}/>
|
<CronButton database={dbItem}/>
|
||||||
<AlertPolicyModal database={dbItem} notificationChannels={activeOrganizationChannels} organizationId={organization.id} />
|
<AlertPolicyModal database={dbItem} notificationChannels={activeOrganizationChannels}
|
||||||
|
organizationId={organization.id}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<BackupButton disable={isAlreadyBackup} databaseId={databaseId}/>
|
<BackupButton disable={isAlreadyBackup} databaseId={databaseId}/>
|
||||||
@@ -115,6 +114,8 @@ export default async function RoutePage(props: PageParams<{
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</PageTitle>
|
</PageTitle>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{dbItem.description && (
|
{dbItem.description && (
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {db} from "@/db";
|
|||||||
import {EDbmsSchema} from "@/db/schema/types";
|
import {EDbmsSchema} from "@/db/schema/types";
|
||||||
import {eq} from "drizzle-orm";
|
import {eq} from "drizzle-orm";
|
||||||
import {isUuidv4} from "@/utils/verify-uuid";
|
import {isUuidv4} from "@/utils/verify-uuid";
|
||||||
|
import {withUpdatedAt} from "@/db/utils";
|
||||||
|
|
||||||
export type databaseAgent = {
|
export type databaseAgent = {
|
||||||
name: string,
|
name: string,
|
||||||
@@ -15,12 +16,13 @@ export type databaseAgent = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type Body = {
|
export type Body = {
|
||||||
|
version: string,
|
||||||
databases: databaseAgent[]
|
databases: databaseAgent[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to test the get file url presigned local
|
// Function to test the get file url presigned local
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const url = await getFileUrlPresignedLocal({fileName:"d4a7fa35-2506-4d01-a612-a8ef2e2cc1c5.dump"})
|
const url = await getFileUrlPresignedLocal({fileName: "d4a7fa35-2506-4d01-a612-a8ef2e2cc1c5.dump"})
|
||||||
return Response.json({
|
return Response.json({
|
||||||
message: url
|
message: url
|
||||||
})
|
})
|
||||||
@@ -60,7 +62,10 @@ export async function POST(
|
|||||||
|
|
||||||
await db
|
await db
|
||||||
.update(drizzleDb.schemas.agent)
|
.update(drizzleDb.schemas.agent)
|
||||||
.set({lastContact: lastContact})
|
.set(withUpdatedAt({
|
||||||
|
version: body.version,
|
||||||
|
lastContact: lastContact
|
||||||
|
}))
|
||||||
.where(eq(drizzleDb.schemas.agent.id, agentId));
|
.where(eq(drizzleDb.schemas.agent.id, agentId));
|
||||||
|
|
||||||
eventEmitter.emit('modification', {update: true});
|
eventEmitter.emit('modification', {update: true});
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "portabase",
|
"name": "portabase",
|
||||||
"version": "1.1.4",
|
"version": "1.1.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack -p 8887",
|
"dev": "next dev --turbopack -p 8887",
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
import {Card, CardContent, CardHeader} from "@/components/ui/card";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { formatDateLastContact } from "@/utils/date-formatting";
|
import {formatDateLastContact} from "@/utils/date-formatting";
|
||||||
import { ConnectionCircle } from "@/components/wrappers/common/connection-circle";
|
import {ConnectionCircle} from "@/components/wrappers/common/connection-circle";
|
||||||
import {Agent} from "@/db/schema/08_agent";
|
import {Agent, AgentWith} from "@/db/schema/08_agent";
|
||||||
|
import {Activity, Database, ShieldCheck} from "lucide-react";
|
||||||
|
|
||||||
export type agentCardProps = {
|
export type agentCardProps = {
|
||||||
data: Agent;
|
data: AgentWith;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AgentCard = (props: agentCardProps) => {
|
export const AgentCard = (props: agentCardProps) => {
|
||||||
const { data: agent } = props;
|
const {data: agent} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={`/dashboard/agents/${agent.id}`}
|
<Link href={`/dashboard/agents/${agent.id}`}
|
||||||
@@ -20,10 +21,31 @@ export const AgentCard = (props: agentCardProps) => {
|
|||||||
<Card className="flex flex-row justify-between">
|
<Card className="flex flex-row justify-between">
|
||||||
<div className="flex-1 text-left">
|
<div className="flex-1 text-left">
|
||||||
<CardHeader className="text-2xl font-bold">{agent.name}</CardHeader>
|
<CardHeader className="text-2xl font-bold">{agent.name}</CardHeader>
|
||||||
<CardContent>Last contact: {formatDateLastContact(agent.lastContact)}</CardContent>
|
<CardContent>
|
||||||
|
<div className="flex flex-wrap items-center gap-x-6 gap-y-2 text-sm text-muted-foreground">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Activity className="h-4 w-4"/>
|
||||||
|
<span>{formatDateLastContact(agent.lastContact)}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Database className="h-4 w-4"/>
|
||||||
|
<span>{agent.databases?.length ?? 0} DB</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ShieldCheck className="h-4 w-4"/>
|
||||||
|
{agent.version ?
|
||||||
|
<span>v{agent.version}</span>
|
||||||
|
:
|
||||||
|
<span>N/A</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center px-4">
|
<div className="flex items-center px-4">
|
||||||
<ConnectionCircle date={agent.lastContact} />
|
<ConnectionCircle date={agent.lastContact}/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "agents" ADD COLUMN "version" text;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -85,6 +85,13 @@
|
|||||||
"when": 1765097874110,
|
"when": 1765097874110,
|
||||||
"tag": "0011_outgoing_blob",
|
"tag": "0011_outgoing_blob",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 12,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1766308683196,
|
||||||
|
"tag": "0012_peaceful_leopardon",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -8,9 +8,6 @@ import {member, OrganizationMember} from "@/db/schema/04_member";
|
|||||||
import {User} from "@/db/schema/02_user";
|
import {User} from "@/db/schema/02_user";
|
||||||
import {timestamps} from "@/db/schema/00_common";
|
import {timestamps} from "@/db/schema/00_common";
|
||||||
import {NotificationChannel, organizationNotificationChannel} from "@/db/schema/09_notification-channel";
|
import {NotificationChannel, organizationNotificationChannel} from "@/db/schema/09_notification-channel";
|
||||||
import {Agent} from "@/db/schema/08_agent";
|
|
||||||
import {AlertPolicy} from "@/db/schema/10_alert-policy";
|
|
||||||
import {Backup, Database, Restoration, RetentionPolicy} from "@/db/schema/07_database";
|
|
||||||
|
|
||||||
export const organization = pgTable("organization", {
|
export const organization = pgTable("organization", {
|
||||||
id: uuid("id").defaultRandom().primaryKey(),
|
id: uuid("id").defaultRandom().primaryKey(),
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
import {boolean, pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
|
import {boolean, pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
|
||||||
import { createSelectSchema } from "drizzle-zod";
|
import {createSelectSchema} from "drizzle-zod";
|
||||||
import { z } from "zod";
|
import {z} from "zod";
|
||||||
import {database} from "@/db/schema/07_database";
|
import {Database, database} from "@/db/schema/07_database";
|
||||||
import {relations} from "drizzle-orm";
|
import {relations} from "drizzle-orm";
|
||||||
import {timestamps} from "@/db/schema/00_common";
|
import {timestamps} from "@/db/schema/00_common";
|
||||||
|
|
||||||
export const agent = pgTable("agents", {
|
export const agent = pgTable("agents", {
|
||||||
id: uuid("id").primaryKey().defaultRandom(),
|
id: uuid("id").primaryKey().defaultRandom(),
|
||||||
slug: text("slug").notNull().unique(),
|
slug: text("slug").notNull().unique(),
|
||||||
|
version: text("version"),
|
||||||
name: text("name").notNull().notNull(),
|
name: text("name").notNull().notNull(),
|
||||||
description: text("description").notNull(),
|
description: text("description").notNull(),
|
||||||
isArchived: boolean("is_archived").default(false),
|
isArchived: boolean("is_archived").default(false),
|
||||||
lastContact: timestamp("last_contact"),
|
lastContact: timestamp("last_contact"),
|
||||||
...timestamps
|
...timestamps
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const agentSchema = createSelectSchema(agent);
|
export const agentSchema = createSelectSchema(agent);
|
||||||
export type Agent = z.infer<typeof agentSchema>;
|
export type Agent = z.infer<typeof agentSchema>;
|
||||||
|
|
||||||
|
|
||||||
export const agentRelations = relations(agent, ({ many }) => ({
|
export const agentRelations = relations(agent, ({many}) => ({
|
||||||
databases: many(database),
|
databases: many(database),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
export type AgentWith = Agent & {
|
||||||
|
databases?: Database[] | null;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user