Initial Phoenix Sankofa Cloud setup

- Complete project structure with Next.js frontend
- GraphQL API backend with Apollo Server
- Portal application with NextAuth
- Crossplane Proxmox provider
- GitOps configurations
- CI/CD pipelines
- Testing infrastructure (Vitest, Jest, Go tests)
- Error handling and monitoring
- Security hardening
- UI component library
- Documentation
This commit is contained in:
defiQUG
2025-11-28 12:54:33 -08:00
commit 6f28146ac3
229 changed files with 43136 additions and 0 deletions

132
docs/api/README.md Normal file
View File

@@ -0,0 +1,132 @@
# API Documentation
## GraphQL API
The Phoenix Sankofa Cloud API is a GraphQL API built with Apollo Server.
### Endpoint
- Development: `http://localhost:4000/graphql`
- Production: `https://api.sankofa.cloud/graphql`
### Authentication
All queries and mutations (except `login`) require authentication via JWT token:
```http
Authorization: Bearer <token>
```
### Schema
See [schema.graphql](./schema.graphql) for the complete GraphQL schema.
### Queries
#### Get Resources
```graphql
query GetResources($filter: ResourceFilter) {
resources(filter: $filter) {
id
name
type
status
site {
id
name
}
}
}
```
#### Get Sites
```graphql
query GetSites {
sites {
id
name
region
status
}
}
```
#### Get Current User
```graphql
query GetMe {
me {
id
email
name
role
}
}
```
### Mutations
#### Login
```graphql
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
user {
id
email
name
}
}
}
```
#### Create Resource
```graphql
mutation CreateResource($input: CreateResourceInput!) {
createResource(input: $input) {
id
name
type
status
}
}
```
### Error Handling
The API returns errors in the standard GraphQL error format:
```json
{
"errors": [
{
"message": "Authentication required",
"extensions": {
"code": "UNAUTHENTICATED"
}
}
]
}
```
### Error Codes
- `UNAUTHENTICATED`: Authentication required
- `FORBIDDEN`: Insufficient permissions
- `NOT_FOUND`: Resource not found
- `VALIDATION_ERROR`: Input validation failed
- `SERVER_ERROR`: Internal server error
### Rate Limiting
- 100 requests per minute per IP
- 1000 requests per hour per authenticated user
### Examples
See [examples.md](./examples.md) for more usage examples.

109
docs/api/examples.md Normal file
View File

@@ -0,0 +1,109 @@
# API Usage Examples
## Authentication
### Login
```javascript
const LOGIN_MUTATION = gql`
mutation Login($email: String!, $password: String!) {
login(email: $email, password: $password) {
token
user {
id
email
name
}
}
}
`
const { data } = await client.mutate({
mutation: LOGIN_MUTATION,
variables: {
email: 'user@example.com',
password: 'password123'
}
})
// Store token
localStorage.setItem('token', data.login.token)
```
## Resources
### Get All Resources
```javascript
const GET_RESOURCES = gql`
query GetResources {
resources {
id
name
type
status
site {
name
region
}
}
}
`
const { data } = await client.query({
query: GET_RESOURCES
})
```
### Create Resource
```javascript
const CREATE_RESOURCE = gql`
mutation CreateResource($input: CreateResourceInput!) {
createResource(input: $input) {
id
name
type
status
}
}
`
const { data } = await client.mutate({
mutation: CREATE_RESOURCE,
variables: {
input: {
name: 'web-server-01',
type: 'VM',
siteId: 'site-id-here',
metadata: {
cpu: 4,
memory: '8Gi'
}
}
}
})
```
## Using React Hooks
```typescript
import { useResources, useCreateResource } from '@/lib/graphql/hooks'
function ResourcesList() {
const { data, loading, error } = useResources()
const { createResource } = useCreateResource()
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{data?.resources.map(resource => (
<div key={resource.id}>{resource.name}</div>
))}
</div>
)
}
```

134
docs/api/schema.graphql Normal file
View File

@@ -0,0 +1,134 @@
# GraphQL Schema
```graphql
scalar DateTime
scalar JSON
type Query {
health: HealthStatus
resources(filter: ResourceFilter): [Resource!]!
resource(id: ID!): Resource
sites: [Site!]!
site(id: ID!): Site
me: User
users: [User!]!
user(id: ID!): User
}
type Mutation {
login(email: String!, password: String!): AuthPayload!
logout: Boolean!
createResource(input: CreateResourceInput!): Resource!
updateResource(id: ID!, input: UpdateResourceInput!): Resource!
deleteResource(id: ID!): Boolean!
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
}
type Subscription {
resourceUpdated(id: ID!): Resource!
resourceCreated: Resource!
resourceDeleted(id: ID!): ID!
}
type HealthStatus {
status: String!
timestamp: DateTime!
version: String!
}
type Resource {
id: ID!
name: String!
type: ResourceType!
status: ResourceStatus!
site: Site!
metadata: JSON
createdAt: DateTime!
updatedAt: DateTime!
}
type Site {
id: ID!
name: String!
region: String!
status: SiteStatus!
resources: [Resource!]!
createdAt: DateTime!
updatedAt: DateTime!
}
type User {
id: ID!
email: String!
name: String!
role: UserRole!
createdAt: DateTime!
updatedAt: DateTime!
}
type AuthPayload {
token: String!
user: User!
}
enum ResourceType {
VM
CONTAINER
STORAGE
NETWORK
}
enum ResourceStatus {
PENDING
PROVISIONING
RUNNING
STOPPED
ERROR
DELETING
}
enum SiteStatus {
ACTIVE
INACTIVE
MAINTENANCE
}
enum UserRole {
ADMIN
USER
VIEWER
}
input ResourceFilter {
type: ResourceType
status: ResourceStatus
siteId: ID
}
input CreateResourceInput {
name: String!
type: ResourceType!
siteId: ID!
metadata: JSON
}
input UpdateResourceInput {
name: String
metadata: JSON
}
input CreateUserInput {
email: String!
name: String!
password: String!
role: UserRole
}
input UpdateUserInput {
name: String
role: UserRole
}
```