refactor: switch landing from DB to api requests
This commit is contained in:
parent
d1806bfd7e
commit
b9a8ca8368
|
|
@ -28,13 +28,16 @@ export default function ApiKeysPage() {
|
|||
router.push('/admin/master');
|
||||
} else {
|
||||
setMasterKey(saved);
|
||||
loadApiKeys();
|
||||
// Load API keys with the saved master key
|
||||
listApiKeys(saved).then(setApiKeys);
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
const loadApiKeys = async () => {
|
||||
const keys = await listApiKeys();
|
||||
if (masterKey) {
|
||||
const keys = await listApiKeys(masterKey);
|
||||
setApiKeys(keys);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
'use server';
|
||||
|
||||
import { listApiKeys as listApiKeysQuery } from '../db/queries/apiKeys';
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
|
||||
|
||||
interface BootstrapResponse {
|
||||
|
|
@ -26,6 +24,48 @@ interface CreateKeyResponse {
|
|||
message: string;
|
||||
}
|
||||
|
||||
interface ListKeysApiResponse {
|
||||
keys: Array<{
|
||||
id: string;
|
||||
type: string;
|
||||
name: string | null;
|
||||
scopes: string[];
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
expiresAt: string | null;
|
||||
lastUsedAt: string | null;
|
||||
createdBy: string | null;
|
||||
organization: {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
email: string;
|
||||
} | null;
|
||||
project: {
|
||||
id: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
} | null;
|
||||
}>;
|
||||
total: number;
|
||||
}
|
||||
|
||||
interface ApiKeyListItem {
|
||||
id: string;
|
||||
keyType: string;
|
||||
name: string | null;
|
||||
scopes: string[];
|
||||
isActive: boolean;
|
||||
createdAt: Date;
|
||||
expiresAt: Date | null;
|
||||
lastUsedAt: Date | null;
|
||||
organizationId: string | null;
|
||||
organizationName: string | null;
|
||||
organizationEmail: string | null;
|
||||
projectId: string | null;
|
||||
projectName: string | null;
|
||||
}
|
||||
|
||||
export async function bootstrapMasterKey(): Promise<{
|
||||
success: boolean;
|
||||
apiKey?: string;
|
||||
|
|
@ -86,9 +126,45 @@ export async function createProjectApiKey(
|
|||
}
|
||||
}
|
||||
|
||||
export async function listApiKeys() {
|
||||
export async function listApiKeys(masterKey: string): Promise<ApiKeyListItem[]> {
|
||||
try {
|
||||
return await listApiKeysQuery();
|
||||
if (!masterKey) {
|
||||
console.error('Master key not provided');
|
||||
return [];
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/api/admin/keys`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': masterKey,
|
||||
},
|
||||
cache: 'no-store',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch API keys:', response.statusText);
|
||||
return [];
|
||||
}
|
||||
|
||||
const data: ListKeysApiResponse = await response.json();
|
||||
|
||||
// Transform nested API response to flat structure for backwards compatibility
|
||||
return data.keys.map((key) => ({
|
||||
id: key.id,
|
||||
keyType: key.type,
|
||||
name: key.name,
|
||||
scopes: key.scopes,
|
||||
isActive: key.isActive,
|
||||
createdAt: new Date(key.createdAt),
|
||||
expiresAt: key.expiresAt ? new Date(key.expiresAt) : null,
|
||||
lastUsedAt: key.lastUsedAt ? new Date(key.lastUsedAt) : null,
|
||||
organizationId: key.organization?.id || null,
|
||||
organizationName: key.organization?.name || null,
|
||||
organizationEmail: key.organization?.email || null,
|
||||
projectId: key.project?.id || null,
|
||||
projectName: key.project?.name || null,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('List keys error:', error);
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
'use server';
|
||||
|
||||
import { getOrganizationByEmail, createOrganization } from '../db/queries/organizations';
|
||||
import { getProjectByName, createProject } from '../db/queries/projects';
|
||||
import type { Organization, Project } from '@banatie/database';
|
||||
|
||||
export async function getOrCreateOrganization(email: string, name: string): Promise<Organization> {
|
||||
// Try to find existing organization
|
||||
const existing = await getOrganizationByEmail(email);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Create new organization
|
||||
return createOrganization({ email, name });
|
||||
}
|
||||
|
||||
export async function getOrCreateProject(organizationId: string, name: string): Promise<Project> {
|
||||
// Try to find existing project
|
||||
const existing = await getProjectByName(organizationId, name);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Create new project
|
||||
return createProject({ organizationId, name });
|
||||
}
|
||||
|
||||
export async function getOrCreateOrgAndProject(
|
||||
email: string,
|
||||
orgName: string,
|
||||
projectName: string,
|
||||
): Promise<{ organization: Organization; project: Project }> {
|
||||
// Get or create organization
|
||||
const organization = await getOrCreateOrganization(email, orgName);
|
||||
|
||||
// Get or create project
|
||||
const project = await getOrCreateProject(organization.id, projectName);
|
||||
|
||||
return { organization, project };
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import postgres from 'postgres';
|
||||
import * as schema from '@banatie/database';
|
||||
|
||||
const connectionString =
|
||||
process.env.DATABASE_URL ||
|
||||
'postgresql://banatie_user:banatie_secure_password@localhost:5460/banatie_db';
|
||||
|
||||
// Create postgres client
|
||||
const client = postgres(connectionString);
|
||||
|
||||
// Create drizzle instance with schema
|
||||
export const db = drizzle(client, { schema });
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { db } from '../client';
|
||||
import { apiKeys, organizations, projects, type ApiKey } from '@banatie/database';
|
||||
import { eq, desc } from 'drizzle-orm';
|
||||
|
||||
export async function listApiKeys() {
|
||||
return db
|
||||
.select({
|
||||
id: apiKeys.id,
|
||||
keyType: apiKeys.keyType,
|
||||
name: apiKeys.name,
|
||||
scopes: apiKeys.scopes,
|
||||
isActive: apiKeys.isActive,
|
||||
createdAt: apiKeys.createdAt,
|
||||
expiresAt: apiKeys.expiresAt,
|
||||
lastUsedAt: apiKeys.lastUsedAt,
|
||||
organizationId: apiKeys.organizationId,
|
||||
organizationName: organizations.name,
|
||||
organizationEmail: organizations.email,
|
||||
projectId: apiKeys.projectId,
|
||||
projectName: projects.name,
|
||||
})
|
||||
.from(apiKeys)
|
||||
.leftJoin(organizations, eq(apiKeys.organizationId, organizations.id))
|
||||
.leftJoin(projects, eq(apiKeys.projectId, projects.id))
|
||||
.orderBy(desc(apiKeys.createdAt))
|
||||
.limit(50);
|
||||
}
|
||||
|
||||
export async function getApiKeysByProject(projectId: string) {
|
||||
return db
|
||||
.select()
|
||||
.from(apiKeys)
|
||||
.where(eq(apiKeys.projectId, projectId))
|
||||
.orderBy(desc(apiKeys.createdAt));
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { db } from '../client';
|
||||
import { organizations, type Organization, type NewOrganization } from '@banatie/database';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export async function getOrganizationByEmail(email: string): Promise<Organization | null> {
|
||||
const [org] = await db
|
||||
.select()
|
||||
.from(organizations)
|
||||
.where(eq(organizations.email, email))
|
||||
.limit(1);
|
||||
|
||||
return org || null;
|
||||
}
|
||||
|
||||
export async function createOrganization(data: NewOrganization): Promise<Organization> {
|
||||
const [org] = await db.insert(organizations).values(data).returning();
|
||||
|
||||
return org!;
|
||||
}
|
||||
|
||||
export async function listOrganizations(): Promise<Organization[]> {
|
||||
return db.select().from(organizations).orderBy(organizations.createdAt);
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
import { db } from '../client';
|
||||
import { projects, type Project, type NewProject } from '@banatie/database';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
|
||||
export async function getProjectByName(
|
||||
organizationId: string,
|
||||
name: string,
|
||||
): Promise<Project | null> {
|
||||
const [project] = await db
|
||||
.select()
|
||||
.from(projects)
|
||||
.where(and(eq(projects.organizationId, organizationId), eq(projects.name, name)))
|
||||
.limit(1);
|
||||
|
||||
return project || null;
|
||||
}
|
||||
|
||||
export async function createProject(data: NewProject): Promise<Project> {
|
||||
const [project] = await db.insert(projects).values(data).returning();
|
||||
|
||||
return project!;
|
||||
}
|
||||
|
||||
export async function listProjectsByOrganization(organizationId: string): Promise<Project[]> {
|
||||
return db
|
||||
.select()
|
||||
.from(projects)
|
||||
.where(eq(projects.organizationId, organizationId))
|
||||
.orderBy(projects.createdAt);
|
||||
}
|
||||
Loading…
Reference in New Issue