Files
whatshooked/PHASE2_PROGRESS.md
Hein 35a548e7e2
Some checks failed
CI / Test (1.23) (push) Failing after -22m40s
CI / Test (1.22) (push) Failing after -22m36s
CI / Build (push) Failing after -23m32s
CI / Lint (push) Failing after -23m7s
refactor(UI): 🏗️ Ui changes and API changes
2026-02-06 17:03:28 +02:00

648 lines
17 KiB
Markdown

# Phase 2 Implementation Progress
## Completed ✅
### 1. Tool Documentation (tooldoc/)
Created comprehensive documentation for all libraries and frameworks:
- **CODE_GUIDELINES.md**: Complete coding standards, project structure, naming conventions, error handling, testing patterns, and best practices
- **RESOLVESPEC.md**: Detailed ResolveSpec integration guide including setup, API patterns, filtering, pagination, and security
- **ORANGURU.md**: Oranguru component library guide for enhanced Mantine components
- **REACT_MANTINE_TANSTACK.md**: Frontend framework integration guide with project structure and examples
### 2. Database Storage Layer (pkg/storage/)
Complete database abstraction with GORM support:
**Models** (storage/models.go):
- User: Authentication and user management
- APIKey: API key-based authentication
- Hook: Webhook registrations with user ownership
- WhatsAppAccount: WhatsApp account configurations per user
- EventLog: Audit trail for all operations
- Session: User session management
- MessageCache: WhatsApp message caching
**Database Management** (storage/db.go):
- PostgreSQL and SQLite support
- Connection pooling
- Auto-migration support
- Health check functionality
**Repository Pattern** (storage/repository.go):
- Generic repository with CRUD operations
- Specialized repositories for each model
- User-specific queries (by username, email)
- API key queries (by key, user)
- Hook and account filtering by user
- Event log queries with time-based filtering
**Seed Data** (storage/seed.go):
- Creates default admin user (username: admin, password: admin123)
- Safe to run multiple times
### 3. Authentication Package (pkg/auth/)
Full-featured authentication system:
**Core Auth** (auth/auth.go):
- JWT token generation and validation
- API key authentication
- Password hashing with bcrypt
- User login with credential verification
- API key creation and revocation
- Permission checking based on roles (admin, user, viewer)
**Middleware** (auth/middleware.go):
- AuthMiddleware: Requires authentication (JWT or API key)
- OptionalAuthMiddleware: Extracts user if present
- RoleMiddleware: Enforces role-based access control
- Context helpers for user extraction
### 4. Web Server Package (pkg/webserver/)
Complete REST API server with authentication:
**Server Setup** (webserver/server.go):
- Gorilla Mux router integration
- Authentication middleware
- Role-based route protection
- Public and protected routes
- Admin-only routes
**Core Handlers** (webserver/handlers.go):
- Health check endpoint
- User login with JWT
- Get current user profile
- Change password
- Create API key
- Revoke API key
- List users (admin)
- Create user (admin)
**CRUD Handlers** (webserver/handlers_crud.go):
- Hook management (list, create, get, update, delete)
- WhatsApp account management (list, create, get, update, delete)
- API key listing
- User management (get, update, delete - admin only)
- Ownership verification for all resources
### 5. API Endpoints
Complete RESTful API:
**Public Endpoints**:
- `GET /health` - Health check
- `POST /api/v1/auth/login` - User login
**Authenticated Endpoints**:
- `GET /api/v1/users/me` - Get current user
- `PUT /api/v1/users/me/password` - Change password
**API Keys**:
- `GET /api/v1/api-keys` - List user's API keys
- `POST /api/v1/api-keys` - Create API key
- `POST /api/v1/api-keys/{id}/revoke` - Revoke API key
**Hooks**:
- `GET /api/v1/hooks` - List user's hooks
- `POST /api/v1/hooks` - Create hook
- `GET /api/v1/hooks/{id}` - Get hook details
- `PUT /api/v1/hooks/{id}` - Update hook
- `DELETE /api/v1/hooks/{id}` - Delete hook
**WhatsApp Accounts**:
- `GET /api/v1/whatsapp-accounts` - List user's accounts
- `POST /api/v1/whatsapp-accounts` - Create account
- `GET /api/v1/whatsapp-accounts/{id}` - Get account details
- `PUT /api/v1/whatsapp-accounts/{id}` - Update account
- `DELETE /api/v1/whatsapp-accounts/{id}` - Delete account
**Admin Endpoints**:
- `GET /api/v1/admin/users` - List all users
- `POST /api/v1/admin/users` - Create user
- `GET /api/v1/admin/users/{id}` - Get user
- `PUT /api/v1/admin/users/{id}` - Update user
- `DELETE /api/v1/admin/users/{id}` - Delete user
### 6. Dependencies Added
- `github.com/bitechdev/ResolveSpec` - REST API framework
- `gorm.io/gorm` - ORM
- `gorm.io/driver/postgres` - PostgreSQL driver
- `gorm.io/driver/sqlite` - SQLite driver
- `github.com/golang-jwt/jwt/v5` - JWT authentication
- `github.com/gorilla/mux` - HTTP router
- `golang.org/x/crypto` - Bcrypt password hashing
- All ResolveSpec transitive dependencies
## Pending 📋
### 7-11. Frontend Implementation
The following items require frontend development:
- React frontend with Mantine and TanStack Start
- Oranguru integration for grids and forms
- User login interface
- API key management UI
- User-level hooks and WhatsApp account management UI
## How to Use
### 1. Database Setup
```go
import "git.warky.dev/wdevs/whatshooked/pkg/storage"
import "git.warky.dev/wdevs/whatshooked/pkg/config"
// Initialize database
cfg := &config.DatabaseConfig{
Type: "postgres", // or "sqlite"
Host: "localhost",
Port: 5432,
Username: "whatshooked",
Password: "password",
Database: "whatshooked",
}
err := storage.Initialize(cfg)
if err != nil {
log.Fatal(err)
}
// Run migrations
err = storage.AutoMigrate()
if err != nil {
log.Fatal(err)
}
// Seed default data (creates admin user)
err = storage.SeedData(context.Background())
if err != nil {
log.Fatal(err)
}
```
### 2. Start Web Server
```go
import "git.warky.dev/wdevs/whatshooked/pkg/webserver"
// Create server with JWT secret
server, err := webserver.NewServer("your-secret-key-here")
if err != nil {
log.Fatal(err)
}
// Start server
log.Fatal(server.Start(":8825"))
```
### 3. API Usage Examples
**Login**:
```bash
curl -X POST http://localhost:8825/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}'
```
**Create Hook** (with JWT):
```bash
curl -X POST http://localhost:8825/api/v1/hooks \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Webhook",
"url": "https://example.com/webhook",
"method": "POST",
"events": ["message.received"],
"active": true
}'
```
**Create API Key**:
```bash
curl -X POST http://localhost:8825/api/v1/api-keys \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Production API Key"}'
```
**Use API Key**:
```bash
curl http://localhost:8825/api/v1/hooks \
-H "Authorization: ApiKey YOUR_API_KEY"
```
## Security Features
1. **Multi-tenancy**: Users can only access their own resources (hooks, API keys, accounts)
2. **Role-based Access Control**: Admin, user, and viewer roles with hierarchical permissions
3. **JWT Authentication**: Secure token-based authentication with 24-hour expiry
4. **API Key Authentication**: Alternative authentication for programmatic access
5. **Password Hashing**: Bcrypt with default cost for secure password storage
6. **Ownership Verification**: All CRUD operations verify resource ownership
7. **Audit Trail**: EventLog table for tracking all operations
## Next Steps
To complete Phase 2, implement the frontend:
1. **Initialize Frontend Project**:
```bash
npm create @tanstack/start@latest
cd frontend
npm install @mantine/core @mantine/hooks @mantine/notifications @mantine/form @mantine/datatable
npm install @warkypublic/oranguru
npm install @tanstack/react-query axios
```
2. **Create Components**:
- Login page
- Dashboard layout with navigation
- User profile page
- API key management page
- Hook management page (with Oranguru DataTable)
- WhatsApp account management page
3. **Integrate with API**:
- Setup axios with JWT token interceptor
- Create API client modules
- Implement TanStack Query for data fetching
- Add error handling and loading states
4. **Build and Deploy**:
- Build frontend: `npm run build`
- Serve static files through Go server
- Configure reverse proxy if needed
## Using Oranguru
1. import { Gridler } from '../Gridler';
- Gridler is the grid component
- GlidlerAPIAdaptorForGoLangv2 is used to connect Gridler to RestHeadSpecAPI
```ts
const GridExample = () => {
const columns: GridlerColumns = [
{
Cell: (row) => {
const process = `${
row?.cql2?.length > 0
? '🔖'
: row?.cql1?.length > 0
? '📕'
: row?.status === 1
? '💡'
: row?.status === 2
? '🔒'
: '⚙️'
} ${String(row?.id_process ?? '0')}`;
return {
data: process,
displayData: process,
status: row?.status,
} as any;
},
id: 'id_process',
title: 'RID',
width: 100,
},
{
id: 'process',
title: 'Process',
tooltip: (buffer) => {
return `Process: ${buffer?.process}\nType: ${buffer?.processtype}\nStatus: ${buffer?.status}`;
},
width: 200,
},
{
id: 'processtype',
title: 'Type',
},
{
disableSort: true,
id: 'status',
title: 'Status',
width: 100,
},
];
return (<Gridler
columns={columns}
height="100%"
keyField="id_process"
onChange={(v) => {
//console.log('GridlerGoAPIExampleEventlog onChange', v);
setValues(v);
}}
ref={ref}
scrollToRowKey={selectRow ? parseInt(selectRow, 10) : undefined}
searchStr={search}
sections={{ ...sections, rightElementDisabled: false }}
selectFirstRowOnMount={true}
selectMode="row"
title="Go API Example"
uniqueid="gridtest"
values={values}
>
<GlidlerAPIAdaptorForGoLangv2
authtoken={apiKey}
options={[{ type: 'preload', value: 'PRO' }]}
//options={[{ type: 'fieldfilter', name: 'process', value: 'test' }]}
url={`${apiUrl}/public/process`}
/>
<Gridler.FormAdaptor
changeOnActiveClick={true}
descriptionField={'process'}
onRequestForm={(request, data) => {
console.log('Form requested', request, data);
//Show form insert,update,delete
}}
/>
</Gridler>
)
}
```
2. Former is a form wrapper that uses react-hook-form to handle forms.
```ts
import { TextInput } from '@mantine/core';
import { useUncontrolled } from '@mantine/hooks';
import { url } from 'inspector';
import { Controller } from 'react-hook-form';
import { TextInputCtrl, NativeSelectCtrl } from '../../FormerControllers';
import { InlineWrapper } from '../../FormerControllers/Inputs/InlineWrapper';
import NumberInputCtrl from '../../FormerControllers/Inputs/NumberInputCtrl';
import { Former } from '../Former';
import { FormerRestHeadSpecAPI } from '../FormerRestHeadSpecAPI';
export const ApiFormData = (props: {
onChange?: (values: Record<string, unknown>) => void;
primeData?: Record<string, unknown>;
values?: Record<string, unknown>;
}) => {
const [values, setValues] = useUncontrolled<Record<string, unknown>>({
defaultValue: { authToken: '', url: '', ...props.primeData },
finalValue: { authToken: '', url: '', ...props.primeData },
onChange: props.onChange,
value: props.values,
});
return (
<Former
disableHTMlForm
id="api-form-data"
layout={{ saveButtonTitle: 'Save URL Parameters' }}
onAPICall={FormerRestHeadSpecAPI({
authToken: authToken,
url: url,
})}
onChange={setValues}
primeData={props.primeData}
request="update"
uniqueKeyField="id"
values={values}
>
<TextInputCtrl label="Test" name="test" />
<NumberInputCtrl label="AgeTest" name="age" />
<InlineWrapper label="Select One" promptWidth={200}>
<NativeSelectCtrl data={['One', 'Two', 'Three']} name="option1" />
</InlineWrapper>
{/* Controllers can be use but we prefer to use the build TextInputCtrl and such for better integration with Former's state management and validation. However, you can also use the Controller component from react-hook-form to integrate custom inputs like this: */}
<Controller
name="url"
render={({ field }) => <TextInput label="URL" type="url" {...field} />}
/>
<Controller
name="authToken"
render={({ field }) => <TextInput label="Auth Token" type="password" {...field} />}
/>
</Former>
);
};
```
3. Controls TextInputCtrl,NumberInputCtrl,NativeSelectCtrl and InlineWrapper
- InlineWrapper is used to display the title on the left and control to the right.
```ts
const Renderable = () => {
return (
<Former>
<Stack h="100%" mih="400px" miw="400px" w="100%">
<TextInputCtrl label="Test" name="test" />
<NumberInputCtrl label="AgeTest" name="age" />
<InlineWrapper label="Select One" promptWidth={200}>
<NativeSelectCtrl data={["One","Two","Three"]} name="option1"/>
</InlineWrapper>
</Stack>
</Former>
);
};
```
4. Boxer is an advanced multi select control with infinite query lookap features for an API.
```ts
// Server-Side Example (Simulated)
export const ServerSide: Story = {
render: () => {
const [value, setValue] = useState<null | string>(null);
// Simulate server-side API call
const handleAPICall = async (params: {
page: number;
pageSize: number;
search?: string;
}): Promise<{ data: Array<BoxerItem>; total: number }> => {
// Simulate network delay
await new Promise((resolve) => setTimeout(resolve, 500));
// Filter based on search
let filteredData = [...sampleData];
if (params.search) {
filteredData = filteredData.filter((item) =>
item.label.toLowerCase().includes(params.search!.toLowerCase())
);
}
// Paginate
const start = params.page * params.pageSize;
const end = start + params.pageSize;
const paginatedData = filteredData.slice(start, end);
return {
data: paginatedData,
total: filteredData.length,
};
};
return (
<div style={{ width: 300 }}>
<Boxer
clearable
dataSource="server"
label="Favorite Fruit (Server-side)"
onAPICall={handleAPICall}
onChange={setValue}
pageSize={10}
placeholder="Select a fruit (Server-side)"
searchable
value={value}
/>
<div style={{ marginTop: 20 }}>
<strong>Selected Value:</strong> {value ?? 'None'}
</div>
</div>
);
},
};
// Multi-Select Example
export const MultiSelect: Story = {
render: () => {
const [value, setValue] = useState<Array<string>>([]);
return (
<div style={{ width: 300 }}>
<Boxer
clearable
data={sampleData}
dataSource="local"
label="Favorite Fruits"
multiSelect
onChange={setValue}
placeholder="Select fruits"
searchable
value={value}
/>
<div style={{ marginTop: 20 }}>
<strong>Selected Values:</strong>{' '}
{value.length > 0 ? value.join(', ') : 'None'}
</div>
</div>
);
},
};
```
## Database Schema
```
users
├── id (PK)
├── username (unique)
├── email (unique)
├── password (hashed)
├── full_name
├── role (admin/user/viewer)
├── active
└── timestamps
api_keys
├── id (PK)
├── user_id (FK)
├── name
├── key (hashed)
├── key_prefix
├── last_used_at
├── expires_at
└── timestamps
hooks
├── id (PK)
├── user_id (FK)
├── name
├── url
├── method
├── headers (JSON)
├── events (JSON array)
├── active
├── secret
└── timestamps
whatsapp_accounts
├── id (PK)
├── user_id (FK)
├── account_type
├── phone_number (unique)
├── display_name
├── status
├── config (JSON)
└── timestamps
event_logs
├── id (PK)
├── user_id
├── event_type
├── entity_type
├── entity_id
├── action
├── data (JSON)
└── created_at
sessions
├── id (PK)
├── user_id (FK)
├── token (hashed)
├── expires_at
└── timestamps
```
## Architecture Benefits
1. **Clean Separation**: Clear boundaries between storage, auth, and web layers
2. **Testable**: Repository pattern and middleware make testing easy
3. **Extensible**: Easy to add new resources following the established patterns
4. **Secure**: Multi-layered security with authentication, authorization, and ownership
5. **Scalable**: Connection pooling and efficient queries
6. **Maintainable**: Consistent patterns and comprehensive documentation
## Summary
Phase 2 backend is **100% complete** with:
- ✅ Comprehensive tool documentation
- ✅ Complete database layer with models and repositories
- ✅ Full authentication system (JWT + API keys)
- ✅ RESTful API with all CRUD operations
- ✅ Role-based access control
- ✅ Multi-tenant architecture
- ✅ Security and audit logging
Only the frontend UI remains to be implemented to complete Phase 2.