docs: nextjs,frontends,get (5 files)

This commit is contained in:
2025-12-26 00:22:16 +00:00
parent f3c32fe137
commit 784591cdf0
5 changed files with 54 additions and 26 deletions

View File

@@ -61,6 +61,12 @@ const verified = scram(password, storedHash);
- Tokens expire after inactivity
- Tokens can be revoked immediately
### Login Lockout & Backoff
- Failed login attempts are tracked per username and rate-limited.
- After `MB_AUTH_LOCKOUT_MAX_ATTEMPTS` within `MB_AUTH_LOCKOUT_WINDOW_MS`, logins lock for `MB_AUTH_LOCKOUT_MS`.
- When locked, the API returns `429` with `Retry-After` to guide clients.
## 🏢 Multi-Tenant Isolation
### Tenant Boundaries
@@ -257,8 +263,7 @@ const hashedKey = sha512(API_KEY); // Hash for storage
## 🔗 Related Documentation
TODO: Security guidelines live at ../security/SECURITY.md; update this link.
- [Security Guidelines](../SECURITY.md) - Security policy
- [Security Guidelines](../security/SECURITY.md) - Security policy
- [5-Level System](./5-level-system.md) - Permission model
- [Database Architecture](./database.md) - Data storage
- [API Development](../guides/api-development.md) - API security

View File

@@ -0,0 +1,3 @@
export { GET } from './handlers/get-package-data'
export { PUT } from './handlers/put-package-data'
export { DELETE } from './handlers/delete-package-data'

View File

@@ -0,0 +1,40 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { getInstalledPackages } from '@/lib/db/packages/get-installed-packages'
import { uninstallPackage } from '@/lib/db/packages/uninstall-package'
import { getPackageCatalogEntry } from '@/lib/packages/server/get-package-catalog-entry'
import { uninstallPackageContent } from '@/lib/packages/server/uninstall-package-content'
interface RouteParams {
params: {
packageId: string
}
}
export async function DELETE(_request: NextRequest, { params }: RouteParams) {
try {
const entry = getPackageCatalogEntry(params.packageId)
if (!entry) {
return NextResponse.json({ error: 'Package not found' }, { status: 404 })
}
const installed = await getInstalledPackages()
if (!installed.some((pkg) => pkg.packageId === params.packageId)) {
return NextResponse.json({ error: 'Package not installed' }, { status: 404 })
}
await uninstallPackageContent(params.packageId, entry.content)
await uninstallPackage(params.packageId)
return NextResponse.json({ deleted: true })
} catch (error) {
console.error('Error uninstalling package:', error)
return NextResponse.json(
{
error: 'Failed to uninstall package',
details: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}

View File

@@ -1,5 +1,6 @@
import { getAdapter } from '../dbal-client'
import type { User } from '../../types/level-types'
import { mapUserRecord } from './map-user-record'
/**
* Get the SuperGod user (instance owner)
@@ -9,17 +10,5 @@ export async function getSuperGod(): Promise<User | null> {
const result = await adapter.list('User', { filter: { isInstanceOwner: true } })
if (result.data.length === 0) return null
const user = result.data[0] as any
return {
id: user.id,
username: user.username,
email: user.email,
role: user.role as any,
profilePicture: user.profilePicture || undefined,
bio: user.bio || undefined,
createdAt: Number(user.createdAt),
tenantId: user.tenantId || undefined,
isInstanceOwner: user.isInstanceOwner,
}
return mapUserRecord(result.data[0] as Record<string, unknown>)
}

View File

@@ -1,5 +1,6 @@
import { getAdapter } from '../dbal-client'
import type { User } from '../../types/level-types'
import { mapUserRecord } from './map-user-record'
export type GetUsersOptions =
| { tenantId: string }
@@ -13,15 +14,5 @@ export async function getUsers(options: GetUsersOptions): Promise<User[]> {
const adapter = getAdapter()
const listOptions = 'tenantId' in options ? { filter: { tenantId: options.tenantId } } : undefined
const result = listOptions ? await adapter.list('User', listOptions) : await adapter.list('User')
return (result.data as any[]).map((u) => ({
id: u.id,
username: u.username,
email: u.email,
role: u.role as any,
profilePicture: u.profilePicture || undefined,
bio: u.bio || undefined,
createdAt: Number(u.createdAt),
tenantId: u.tenantId || undefined,
isInstanceOwner: u.isInstanceOwner,
}))
return (result.data as Record<string, unknown>[]).map((user) => mapUserRecord(user))
}