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');
|
router.push('/admin/master');
|
||||||
} else {
|
} else {
|
||||||
setMasterKey(saved);
|
setMasterKey(saved);
|
||||||
loadApiKeys();
|
// Load API keys with the saved master key
|
||||||
|
listApiKeys(saved).then(setApiKeys);
|
||||||
}
|
}
|
||||||
}, [router]);
|
}, [router]);
|
||||||
|
|
||||||
const loadApiKeys = async () => {
|
const loadApiKeys = async () => {
|
||||||
const keys = await listApiKeys();
|
if (masterKey) {
|
||||||
setApiKeys(keys);
|
const keys = await listApiKeys(masterKey);
|
||||||
|
setApiKeys(keys);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { listApiKeys as listApiKeysQuery } from '../db/queries/apiKeys';
|
|
||||||
|
|
||||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
|
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
|
||||||
|
|
||||||
interface BootstrapResponse {
|
interface BootstrapResponse {
|
||||||
|
|
@ -26,6 +24,48 @@ interface CreateKeyResponse {
|
||||||
message: string;
|
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<{
|
export async function bootstrapMasterKey(): Promise<{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
apiKey?: string;
|
apiKey?: string;
|
||||||
|
|
@ -86,9 +126,45 @@ export async function createProjectApiKey(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listApiKeys() {
|
export async function listApiKeys(masterKey: string): Promise<ApiKeyListItem[]> {
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
console.error('List keys error:', error);
|
console.error('List keys error:', error);
|
||||||
return [];
|
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