Skip to content

Commit 3d97cf5

Browse files
committed
Merge branch 'next' of github.com:devforth/adminforth into next
2 parents 3998f16 + d334163 commit 3d97cf5

6 files changed

Lines changed: 105 additions & 13 deletions

File tree

adminforth/dataConnectors/baseConnector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
4444
return this.client;
4545
}
4646

47-
setupClient(url: string): Promise<void> {
47+
setupClient(url: string, options?: { recovery?: boolean }): Promise<void> {
4848
throw new Error('Method not implemented.');
4949
}
5050

adminforth/dataConnectors/postgres.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,35 @@ types.setTypeParser(1082, (val) => val); // DATE
1616

1717
class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDataSourceConnector {
1818

19-
async setupClient(url: string): Promise<void> {
19+
async setupClient(url: string, options?: { recovery?: boolean }): Promise<void> {
2020
this.client = new Pool({
2121
connectionString: url
2222
});
23-
try {
24-
await this.client.connect();
25-
this.client.on('error', async (err) => {
26-
afLogger.error(`Postgres error: ${err.message} ${err.stack}`);
27-
this.client.end();
28-
await new Promise((resolve) => { setTimeout(resolve, 1000) });
29-
this.setupClient(url);
23+
24+
const selfHeal = options?.recovery !== false;
25+
26+
if (selfHeal) {
27+
this.client.on('error', (err) => {
28+
afLogger.error(`Postgres pool idle client error (pool self-heals on next query): ${err.message} ${err.stack}`);
3029
});
31-
} catch (e) {
32-
afLogger.error(`Failed to connect to Postgres ${e}`);
30+
try {
31+
const client = await this.client.connect();
32+
client.release();
33+
} catch (e) {
34+
afLogger.error(`Failed to connect to Postgres ${e}`);
35+
}
36+
} else {
37+
try {
38+
await this.client.connect();
39+
this.client.on('error', async (err) => {
40+
afLogger.error(`Postgres error: ${err.message} ${err.stack}`);
41+
this.client.end();
42+
await new Promise((resolve) => { setTimeout(resolve, 1000) });
43+
this.setupClient(url, options);
44+
});
45+
} catch (e) {
46+
afLogger.error(`Failed to connect to Postgres ${e}`);
47+
}
3348
}
3449
}
3550

adminforth/documentation/docs/tutorial/02-glossary.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,25 @@ It used to:
1515

1616
There might be several datasources in the system for various databases e.g. One datasource to Mongo DBs and one to Postgres DB.
1717

18+
### connectionRecovery
19+
20+
For PostgreSQL datasources AdminForth keeps a connection pool. The optional `connectionRecovery` flag controls how the connector reacts when that connection drops (DB restart, failover, network blip, etc.):
21+
22+
```ts
23+
dataSources: [
24+
{
25+
id: 'maindb',
26+
url: `${process.env.DATABASE_URL}`,
27+
connectionRecovery: true, // default
28+
},
29+
],
30+
```
31+
32+
- `true` (default, recommended) — **self-heal mode.** The pool recovers automatically: a dead idle connection is dropped and a fresh one is transparently opened on the next query, so the app keeps working without a manual restart. Queries that were in-flight at the moment of the outage will fail, but subsequent queries succeed once the database is back.
33+
- `false`**legacy mode.** On a connection error the pool is destroyed and recreated after 1 second. If the outage outlasts that retry, the app can be left with a permanently dead pool and require a manual restart. Kept only for backward compatibility.
34+
35+
This flag is currently honored by the PostgreSQL connector; other connectors rely on their driver's built-in recovery.
36+
1837
## resource
1938

2039
A [Resource](/docs/api/Back/interfaces/AdminForthResource.md) is a AdminForth representation of a table or collection in database. One resource is one table in the database. Resource has `table` property which should be equal to the name of the table in the database.

adminforth/documentation/docs/tutorial/08-Plugins/17-bulk-ai-flow.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,31 @@ new BulkAiFlowPlugin({
283283
}),
284284
```
285285
286+
## Image generation quality
287+
288+
`ImageGenerationAdapterOpenAI` accepts an `extraParams` object that is passed directly to the OpenAI API. The most useful option here is `quality`, which controls the fidelity and cost of each generated image:
289+
290+
| Value | Description |
291+
|-------|-------------|
292+
| `'low'` | Fastest generation, lowest cost. Good for drafts or high-volume batch jobs where speed matters more than visual fidelity. |
293+
| `'medium'` | Balanced quality and speed. A sensible default for most use cases. |
294+
| `'high'` | Best image quality, slowest and most expensive. Use for final promotional assets or when visual detail is critical. |
295+
296+
```ts
297+
new ImageGenerationAdapterOpenAI({
298+
openAiApiKey: process.env.OPENAI_API_KEY as string,
299+
model: 'gpt-image-1',
300+
//diff-add
301+
extraParams: {
302+
//diff-add
303+
quality: 'low', // 'low' | 'medium' | 'high'
304+
//diff-add
305+
},
306+
}),
307+
```
308+
309+
> ☝️ `'low'` quality is a great starting point when processing large datasets — you can always re-run generation with `'high'` for selected records once you're happy with the prompts.
310+
286311
## Rate Limiting and Best Practices
287312
288313
- Use `rateLimit` for individual image generation operations and for the bulk image generation
@@ -429,6 +454,21 @@ If you are processing large sets of data, you might want to limit the number of
429454
430455
And there won't be more than 5 parallel requests being handled.
431456
457+
## Controlling page size in the generation dialog
458+
459+
When users trigger bulk generation, records are displayed as cards in a paginated dialog. By default, 6 cards are shown per page. Use `pageSize` to tune this — lower values help when cards contain large images or long text, keeping the dialog fast and reviewable:
460+
461+
```ts
462+
new BulkAiFlowPlugin({
463+
actionName: 'Generate description and Price',
464+
465+
//diff-add
466+
pageSize: 10, // default is 6
467+
468+
// ...
469+
}),
470+
```
471+
432472
## Confirming long-running generations
433473
434474
For very large datasets, you can pause generation at specific checkpoints so users can review results before continuing.

adminforth/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,10 @@ class AdminForth implements IAdminForth {
513513

514514
await Promise.all(Object.keys(this.connectors).map(async (dataSourceId) => {
515515
try {
516-
await this.connectors[dataSourceId].setupClient(this.config.dataSources.find((ds) => ds.id === dataSourceId).url);
516+
await this.connectors[dataSourceId].setupClient(
517+
this.config.dataSources.find((ds) => ds.id === dataSourceId).url,
518+
{ recovery: this.config.dataSources.find((ds) => ds.id === dataSourceId).connectionRecovery !== false }
519+
);
517520
} catch (e) {
518521
afLogger.error(`Error while connecting to datasource '${dataSourceId}': ${e}`);
519522
}

adminforth/types/Back.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,10 @@ export interface IAdminForthDataSourceConnector {
273273
/**
274274
* Function to setup client connection to database.
275275
* @param url URL to database. Examples: clickhouse://demo:demo@localhost:8125/demo
276+
* @param options Optional connection options. `recovery` mirrors the dataSource
277+
* `connectionRecovery` flag (defaults to true when omitted).
276278
*/
277-
setupClient(url: string): Promise<void>;
279+
setupClient(url: string, options?: { recovery?: boolean }): Promise<void>;
278280

279281
/**
280282
* Function to get all tables from database.
@@ -1245,6 +1247,19 @@ export type AdminForthDataSource = {
12451247
* - SQLite: `sqlite://<path>`
12461248
*/
12471249
url: string,
1250+
1251+
/**
1252+
* Controls how the connector reacts to a dropped database connection.
1253+
* Currently honored by the PostgreSQL connector.
1254+
*
1255+
* - `true` (default): self-heal mode. The connection pool recovers automatically — when an
1256+
* idle connection dies (DB restart, failover, network blip, etc.) it is dropped and a fresh
1257+
* one is opened on the next query, so the app keeps working without a manual restart.
1258+
* - `false`: legacy mode. On a connection error the pool is destroyed and recreated after 1s.
1259+
* If the outage outlasts that retry the app can be left with a permanently dead pool and
1260+
* require a manual restart. Kept only for backward compatibility.
1261+
*/
1262+
connectionRecovery?: boolean,
12481263
}
12491264

12501265
type AdminForthPageDeclaration = {

0 commit comments

Comments
 (0)