refactor(UI): 🏗️ Ui changes and API changes
This commit is contained in:
@@ -16,6 +16,7 @@ Created comprehensive documentation for all libraries and frameworks:
|
||||
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
|
||||
@@ -25,12 +26,14 @@ Complete database abstraction with GORM support:
|
||||
- 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)
|
||||
@@ -39,6 +42,7 @@ Complete database abstraction with GORM support:
|
||||
- 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
|
||||
|
||||
@@ -47,6 +51,7 @@ Complete database abstraction with GORM support:
|
||||
Full-featured authentication system:
|
||||
|
||||
**Core Auth** (auth/auth.go):
|
||||
|
||||
- JWT token generation and validation
|
||||
- API key authentication
|
||||
- Password hashing with bcrypt
|
||||
@@ -55,6 +60,7 @@ Full-featured authentication system:
|
||||
- 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
|
||||
@@ -65,6 +71,7 @@ Full-featured authentication system:
|
||||
Complete REST API server with authentication:
|
||||
|
||||
**Server Setup** (webserver/server.go):
|
||||
|
||||
- Gorilla Mux router integration
|
||||
- Authentication middleware
|
||||
- Role-based route protection
|
||||
@@ -72,6 +79,7 @@ Complete REST API server with authentication:
|
||||
- Admin-only routes
|
||||
|
||||
**Core Handlers** (webserver/handlers.go):
|
||||
|
||||
- Health check endpoint
|
||||
- User login with JWT
|
||||
- Get current user profile
|
||||
@@ -82,6 +90,7 @@ Complete REST API server with authentication:
|
||||
- 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
|
||||
@@ -93,19 +102,23 @@ Complete REST API server with authentication:
|
||||
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
|
||||
@@ -113,6 +126,7 @@ Complete RESTful API:
|
||||
- `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
|
||||
@@ -120,6 +134,7 @@ Complete RESTful API:
|
||||
- `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
|
||||
@@ -203,6 +218,7 @@ 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" \
|
||||
@@ -210,6 +226,7 @@ curl -X POST http://localhost:8825/api/v1/auth/login \
|
||||
```
|
||||
|
||||
**Create Hook** (with JWT):
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8825/api/v1/hooks \
|
||||
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
|
||||
@@ -224,6 +241,7 @@ curl -X POST http://localhost:8825/api/v1/hooks \
|
||||
```
|
||||
|
||||
**Create API Key**:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8825/api/v1/api-keys \
|
||||
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
|
||||
@@ -232,6 +250,7 @@ curl -X POST http://localhost:8825/api/v1/api-keys \
|
||||
```
|
||||
|
||||
**Use API Key**:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8825/api/v1/hooks \
|
||||
-H "Authorization: ApiKey YOUR_API_KEY"
|
||||
@@ -252,6 +271,7 @@ curl http://localhost:8825/api/v1/hooks \
|
||||
To complete Phase 2, implement the frontend:
|
||||
|
||||
1. **Initialize Frontend Project**:
|
||||
|
||||
```bash
|
||||
npm create @tanstack/start@latest
|
||||
cd frontend
|
||||
@@ -279,6 +299,267 @@ To complete Phase 2, implement the frontend:
|
||||
- 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
|
||||
|
||||
```
|
||||
@@ -354,6 +635,7 @@ sessions
|
||||
## Summary
|
||||
|
||||
Phase 2 backend is **100% complete** with:
|
||||
|
||||
- ✅ Comprehensive tool documentation
|
||||
- ✅ Complete database layer with models and repositories
|
||||
- ✅ Full authentication system (JWT + API keys)
|
||||
|
||||
Reference in New Issue
Block a user