@deallony/shared 1.2.67 → 1.2.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/constants/routes/app.d.ts +3 -0
- package/dist/cjs/constants/routes/app.js +1 -0
- package/dist/cjs/dto/ai/gemini-api-key.dto.d.ts +4 -1
- package/dist/cjs/schema/ad/save-ad.schema.d.ts +31 -0
- package/dist/cjs/schema/ad/save-ad.schema.js +22 -1
- package/dist/cjs/schema/ai/gemini-api-key.schema.d.ts +46 -3
- package/dist/cjs/schema/ai/gemini-api-key.schema.js +36 -9
- package/dist/cjs/schema/communication/index.d.ts +1 -0
- package/dist/cjs/schema/communication/index.js +1 -0
- package/dist/cjs/schema/communication/offer.schema.d.ts +49 -0
- package/dist/cjs/schema/communication/offer.schema.js +31 -0
- package/dist/cjs/types/ad/Ad.d.ts +21 -0
- package/dist/cjs/types/ad-common/PriceType.d.ts +2 -0
- package/dist/cjs/types/common/MetaData.d.ts +3 -0
- package/dist/cjs/types/communication/Chat.d.ts +30 -1
- package/dist/cjs/types/communication/Chat.js +10 -1
- package/dist/cjs/types/integrations/GeminiApiKey.d.ts +11 -2
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/rentPrices.d.ts +8 -0
- package/dist/cjs/utils/rentPrices.js +33 -0
- package/dist/esm/constants/routes/app.js +1 -0
- package/dist/esm/schema/ad/save-ad.schema.js +21 -0
- package/dist/esm/schema/ai/gemini-api-key.schema.js +35 -8
- package/dist/esm/schema/communication/index.js +1 -0
- package/dist/esm/schema/communication/offer.schema.js +28 -0
- package/dist/esm/types/communication/Chat.js +9 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/rentPrices.js +26 -0
- package/package.json +58 -58
|
@@ -300,6 +300,8 @@ export declare const APP_ROUTES: {
|
|
|
300
300
|
by_message: string;
|
|
301
301
|
by_whatsapp: string;
|
|
302
302
|
phone: string;
|
|
303
|
+
/** Rent ads: JSON of [{ price_type_id, price }]; the modal then sends a rent offer */
|
|
304
|
+
rent_prices?: string;
|
|
303
305
|
}) => {
|
|
304
306
|
pathname: string;
|
|
305
307
|
params: {
|
|
@@ -312,6 +314,7 @@ export declare const APP_ROUTES: {
|
|
|
312
314
|
by_whatsapp: string;
|
|
313
315
|
phone: string;
|
|
314
316
|
ad_price: number;
|
|
317
|
+
rent_prices: string | undefined;
|
|
315
318
|
};
|
|
316
319
|
};
|
|
317
320
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { createGeminiApiKeySchema, geminiApiKeyIdBodySchema, leasedGeminiApiKeySchema, leaseGeminiApiKeySchema, reorderGeminiApiKeysSchema, reportGeminiApiKeyUsageSchema, updateGeminiApiKeySchema } from '../../schema/ai/gemini-api-key.schema';
|
|
2
|
+
import { aiKeySettingsSchema, aiProviderSchema, aiKeySelectionModeSchema, createGeminiApiKeySchema, geminiApiKeyIdBodySchema, leasedGeminiApiKeySchema, leaseGeminiApiKeySchema, reorderGeminiApiKeysSchema, reportGeminiApiKeyUsageSchema, updateGeminiApiKeySchema } from '../../schema/ai/gemini-api-key.schema';
|
|
3
3
|
export type CreateGeminiApiKeyDto = z.input<typeof createGeminiApiKeySchema>;
|
|
4
4
|
export type UpdateGeminiApiKeyDto = z.input<typeof updateGeminiApiKeySchema>;
|
|
5
5
|
export type GeminiApiKeyIdBodyDto = z.input<typeof geminiApiKeyIdBodySchema>;
|
|
@@ -7,3 +7,6 @@ export type ReorderGeminiApiKeysDto = z.input<typeof reorderGeminiApiKeysSchema>
|
|
|
7
7
|
export type LeaseGeminiApiKeyDto = z.input<typeof leaseGeminiApiKeySchema>;
|
|
8
8
|
export type LeasedGeminiApiKey = z.infer<typeof leasedGeminiApiKeySchema>;
|
|
9
9
|
export type ReportGeminiApiKeyUsageDto = z.input<typeof reportGeminiApiKeyUsageSchema>;
|
|
10
|
+
export type AiProvider = z.infer<typeof aiProviderSchema>;
|
|
11
|
+
export type AiKeySelectionMode = z.infer<typeof aiKeySelectionModeSchema>;
|
|
12
|
+
export type AiKeySettingsDto = z.input<typeof aiKeySettingsSchema>;
|
|
@@ -6,6 +6,17 @@ export declare const subjectTypeSchema: z.ZodEnum<{
|
|
|
6
6
|
used_products: "used_products";
|
|
7
7
|
}>;
|
|
8
8
|
export declare const adMediaSchema: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodAny>>>;
|
|
9
|
+
/** One rent price per period. Backend checks the period belongs to "For Rent". */
|
|
10
|
+
export declare const adRentPriceSchema: z.ZodObject<{
|
|
11
|
+
price_type_id: z.ZodCoercedNumber<unknown>;
|
|
12
|
+
price: z.ZodCoercedNumber<unknown>;
|
|
13
|
+
is_main: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export declare const adRentPricesSchema: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
16
|
+
price_type_id: z.ZodCoercedNumber<unknown>;
|
|
17
|
+
price: z.ZodCoercedNumber<unknown>;
|
|
18
|
+
is_main: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
|
|
19
|
+
}, z.core.$strip>>>>;
|
|
9
20
|
export declare const createAdSchema: z.ZodObject<{
|
|
10
21
|
motor: z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
11
22
|
ad_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
@@ -98,6 +109,11 @@ export declare const createAdSchema: z.ZodObject<{
|
|
|
98
109
|
ad_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
99
110
|
admin_boundary_id: z.ZodCoercedNumber<unknown>;
|
|
100
111
|
}, z.core.$strip>>>>;
|
|
112
|
+
rent_prices: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
113
|
+
price_type_id: z.ZodCoercedNumber<unknown>;
|
|
114
|
+
price: z.ZodCoercedNumber<unknown>;
|
|
115
|
+
is_main: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
|
|
116
|
+
}, z.core.$strip>>>>;
|
|
101
117
|
user_id: z.ZodCoercedNumber<unknown>;
|
|
102
118
|
id: z.ZodOptional<z.ZodNull>;
|
|
103
119
|
}, z.core.$strip>;
|
|
@@ -197,6 +213,11 @@ export declare const updateAdSchema: z.ZodObject<{
|
|
|
197
213
|
ad_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
198
214
|
admin_boundary_id: z.ZodCoercedNumber<unknown>;
|
|
199
215
|
}, z.core.$strip>>>>;
|
|
216
|
+
rent_prices: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
217
|
+
price_type_id: z.ZodCoercedNumber<unknown>;
|
|
218
|
+
price: z.ZodCoercedNumber<unknown>;
|
|
219
|
+
is_main: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
|
|
220
|
+
}, z.core.$strip>>>>;
|
|
200
221
|
user_id: z.ZodCoercedNumber<unknown>;
|
|
201
222
|
}, z.core.$strip>;
|
|
202
223
|
export declare const saveAdSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -291,6 +312,11 @@ export declare const saveAdSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
291
312
|
ad_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
292
313
|
admin_boundary_id: z.ZodCoercedNumber<unknown>;
|
|
293
314
|
}, z.core.$strip>>>>;
|
|
315
|
+
rent_prices: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
316
|
+
price_type_id: z.ZodCoercedNumber<unknown>;
|
|
317
|
+
price: z.ZodCoercedNumber<unknown>;
|
|
318
|
+
is_main: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
|
|
319
|
+
}, z.core.$strip>>>>;
|
|
294
320
|
user_id: z.ZodCoercedNumber<unknown>;
|
|
295
321
|
id: z.ZodOptional<z.ZodNull>;
|
|
296
322
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -389,5 +415,10 @@ export declare const saveAdSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
389
415
|
ad_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
390
416
|
admin_boundary_id: z.ZodCoercedNumber<unknown>;
|
|
391
417
|
}, z.core.$strip>>>>;
|
|
418
|
+
rent_prices: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
419
|
+
price_type_id: z.ZodCoercedNumber<unknown>;
|
|
420
|
+
price: z.ZodCoercedNumber<unknown>;
|
|
421
|
+
is_main: z.ZodOptional<z.ZodCoercedBoolean<unknown>>;
|
|
422
|
+
}, z.core.$strip>>>>;
|
|
392
423
|
user_id: z.ZodCoercedNumber<unknown>;
|
|
393
424
|
}, z.core.$strip>]>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.saveAdSchema = exports.updateAdSchema = exports.createAdSchema = exports.adMediaSchema = exports.subjectTypeSchema = void 0;
|
|
3
|
+
exports.saveAdSchema = exports.updateAdSchema = exports.createAdSchema = exports.adRentPricesSchema = exports.adRentPriceSchema = exports.adMediaSchema = exports.subjectTypeSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
const AdMedia_1 = require("../../constants/ad/AdMedia");
|
|
6
6
|
const adMedia_1 = require("../../utils/adMedia");
|
|
@@ -166,6 +166,26 @@ exports.adMediaSchema = zod_1.z
|
|
|
166
166
|
})
|
|
167
167
|
.optional()
|
|
168
168
|
.nullable();
|
|
169
|
+
/** One rent price per period. Backend checks the period belongs to "For Rent". */
|
|
170
|
+
exports.adRentPriceSchema = zod_1.z.object({
|
|
171
|
+
price_type_id: zod_1.z.coerce.number().int().positive(),
|
|
172
|
+
price: zod_1.z.coerce.number().positive(),
|
|
173
|
+
is_main: zod_1.z.coerce.boolean().optional(),
|
|
174
|
+
});
|
|
175
|
+
exports.adRentPricesSchema = zod_1.z
|
|
176
|
+
.array(exports.adRentPriceSchema)
|
|
177
|
+
.max(10)
|
|
178
|
+
.superRefine((prices, ctx) => {
|
|
179
|
+
const ids = prices.map((item) => item.price_type_id);
|
|
180
|
+
if (new Set(ids).size !== ids.length) {
|
|
181
|
+
ctx.addIssue({ code: "custom", message: "Each rent period can be listed once" });
|
|
182
|
+
}
|
|
183
|
+
if (prices.filter((item) => item.is_main).length > 1) {
|
|
184
|
+
ctx.addIssue({ code: "custom", message: "Only one rent price can be the main price" });
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
.optional()
|
|
188
|
+
.nullable();
|
|
169
189
|
const baseAdCommon = zod_1.z.object({
|
|
170
190
|
title: zod_1.z.string().trim().optional(),
|
|
171
191
|
status_id: zod_1.z.coerce.number().int().positive().optional(),
|
|
@@ -197,6 +217,7 @@ const baseAdCommon = zod_1.z.object({
|
|
|
197
217
|
company_id: zod_1.z.coerce.number().int().positive().optional().nullable(),
|
|
198
218
|
expires_at: zod_1.z.iso.datetime().optional().nullable(),
|
|
199
219
|
adLocations: zod_1.z.array(adLocationSchema).optional().nullable(),
|
|
220
|
+
rent_prices: exports.adRentPricesSchema,
|
|
200
221
|
});
|
|
201
222
|
exports.createAdSchema = zod_1.z.object({
|
|
202
223
|
user_id: zod_1.z.coerce.number().int().positive(),
|
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
/** Gemini
|
|
2
|
+
/** Which company's API a key belongs to: Google Gemini or Anthropic Claude */
|
|
3
|
+
export declare const AI_PROVIDERS: readonly ["gemini", "anthropic"];
|
|
4
|
+
export declare const aiProviderSchema: z.ZodEnum<{
|
|
5
|
+
gemini: "gemini";
|
|
6
|
+
anthropic: "anthropic";
|
|
7
|
+
}>;
|
|
8
|
+
/** Models offered in the dashboard for each provider; any other model ID can still be typed */
|
|
9
|
+
export declare const AI_PROVIDER_MODELS: Record<(typeof AI_PROVIDERS)[number], readonly string[]>;
|
|
10
|
+
/**
|
|
11
|
+
* How backend-core picks a key: `priority` always takes the first available key in the dashboard
|
|
12
|
+
* order, `balanced` takes the key with the fewest tokens used today.
|
|
13
|
+
*/
|
|
14
|
+
export declare const AI_KEY_SELECTION_MODES: readonly ["priority", "balanced"];
|
|
15
|
+
export declare const aiKeySelectionModeSchema: z.ZodEnum<{
|
|
16
|
+
priority: "priority";
|
|
17
|
+
balanced: "balanced";
|
|
18
|
+
}>;
|
|
19
|
+
/** Default model ID of a key, e.g. gemini-3.1-flash-lite or claude-haiku-5-5 */
|
|
3
20
|
export declare const geminiModelNameSchema: z.ZodString;
|
|
4
21
|
export declare const createGeminiApiKeySchema: z.ZodObject<{
|
|
5
22
|
name: z.ZodString;
|
|
23
|
+
provider: z.ZodDefault<z.ZodEnum<{
|
|
24
|
+
gemini: "gemini";
|
|
25
|
+
anthropic: "anthropic";
|
|
26
|
+
}>>;
|
|
6
27
|
api_key: z.ZodString;
|
|
7
28
|
model_name: z.ZodString;
|
|
8
29
|
is_active: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -11,6 +32,10 @@ export declare const createGeminiApiKeySchema: z.ZodObject<{
|
|
|
11
32
|
export declare const updateGeminiApiKeySchema: z.ZodObject<{
|
|
12
33
|
id: z.ZodCoercedNumber<unknown>;
|
|
13
34
|
name: z.ZodOptional<z.ZodString>;
|
|
35
|
+
provider: z.ZodOptional<z.ZodEnum<{
|
|
36
|
+
gemini: "gemini";
|
|
37
|
+
anthropic: "anthropic";
|
|
38
|
+
}>>;
|
|
14
39
|
api_key: z.ZodOptional<z.ZodString>;
|
|
15
40
|
model_name: z.ZodOptional<z.ZodString>;
|
|
16
41
|
is_active: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -22,16 +47,34 @@ export declare const geminiApiKeyIdBodySchema: z.ZodObject<{
|
|
|
22
47
|
export declare const reorderGeminiApiKeysSchema: z.ZodObject<{
|
|
23
48
|
ids: z.ZodArray<z.ZodCoercedNumber<unknown>>;
|
|
24
49
|
}, z.core.$strip>;
|
|
25
|
-
|
|
50
|
+
export declare const aiKeySettingsSchema: z.ZodObject<{
|
|
51
|
+
selection_mode: z.ZodEnum<{
|
|
52
|
+
priority: "priority";
|
|
53
|
+
balanced: "balanced";
|
|
54
|
+
}>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
/**
|
|
57
|
+
* Ask backend-core for the next available key. `exclude_ids` are keys that already failed for
|
|
58
|
+
* this call, so a retry moves on to the next key in the order.
|
|
59
|
+
*/
|
|
26
60
|
export declare const leaseGeminiApiKeySchema: z.ZodObject<{
|
|
27
61
|
model_name: z.ZodOptional<z.ZodString>;
|
|
62
|
+
providers: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
63
|
+
gemini: "gemini";
|
|
64
|
+
anthropic: "anthropic";
|
|
65
|
+
}>>>;
|
|
66
|
+
exclude_ids: z.ZodOptional<z.ZodArray<z.ZodCoercedNumber<unknown>>>;
|
|
28
67
|
}, z.core.$strip>;
|
|
29
68
|
export declare const leasedGeminiApiKeySchema: z.ZodObject<{
|
|
30
69
|
id: z.ZodNumber;
|
|
70
|
+
provider: z.ZodEnum<{
|
|
71
|
+
gemini: "gemini";
|
|
72
|
+
anthropic: "anthropic";
|
|
73
|
+
}>;
|
|
31
74
|
api_key: z.ZodString;
|
|
32
75
|
model_name: z.ZodString;
|
|
33
76
|
}, z.core.$strip>;
|
|
34
|
-
/** Sent after every
|
|
77
|
+
/** Sent after every AI call made with a leased key. */
|
|
35
78
|
export declare const reportGeminiApiKeyUsageSchema: z.ZodObject<{
|
|
36
79
|
id: z.ZodCoercedNumber<unknown>;
|
|
37
80
|
total_tokens: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.reportGeminiApiKeyUsageSchema = exports.leasedGeminiApiKeySchema = exports.leaseGeminiApiKeySchema = exports.reorderGeminiApiKeysSchema = exports.geminiApiKeyIdBodySchema = exports.updateGeminiApiKeySchema = exports.createGeminiApiKeySchema = exports.geminiModelNameSchema = void 0;
|
|
3
|
+
exports.reportGeminiApiKeyUsageSchema = exports.leasedGeminiApiKeySchema = exports.leaseGeminiApiKeySchema = exports.aiKeySettingsSchema = exports.reorderGeminiApiKeysSchema = exports.geminiApiKeyIdBodySchema = exports.updateGeminiApiKeySchema = exports.createGeminiApiKeySchema = exports.geminiModelNameSchema = exports.aiKeySelectionModeSchema = exports.AI_KEY_SELECTION_MODES = exports.AI_PROVIDER_MODELS = exports.aiProviderSchema = exports.AI_PROVIDERS = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* The dashboard manages them through the admin schemas; other services lease a
|
|
8
|
-
* the tokens they used through the internal schemas.
|
|
6
|
+
* AI API keys (Gemini and Claude) are stored in backend-core (one table) and shared by every
|
|
7
|
+
* Deallony service. The dashboard manages them through the admin schemas; other services lease a
|
|
8
|
+
* key and report the tokens they used through the internal schemas.
|
|
9
9
|
*/
|
|
10
10
|
const geminiApiKeyIdSchema = zod_1.z.coerce.number().int().positive();
|
|
11
|
-
/** Gemini
|
|
11
|
+
/** Which company's API a key belongs to: Google Gemini or Anthropic Claude */
|
|
12
|
+
exports.AI_PROVIDERS = ['gemini', 'anthropic'];
|
|
13
|
+
exports.aiProviderSchema = zod_1.z.enum(exports.AI_PROVIDERS);
|
|
14
|
+
/** Models offered in the dashboard for each provider; any other model ID can still be typed */
|
|
15
|
+
exports.AI_PROVIDER_MODELS = {
|
|
16
|
+
gemini: ['gemini-3.5-flash-lite', 'gemini-3.1-flash-lite'],
|
|
17
|
+
anthropic: ['claude-haiku-5-5', 'claude-sonnet-5-5', 'claude-opus-5-5'],
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* How backend-core picks a key: `priority` always takes the first available key in the dashboard
|
|
21
|
+
* order, `balanced` takes the key with the fewest tokens used today.
|
|
22
|
+
*/
|
|
23
|
+
exports.AI_KEY_SELECTION_MODES = ['priority', 'balanced'];
|
|
24
|
+
exports.aiKeySelectionModeSchema = zod_1.z.enum(exports.AI_KEY_SELECTION_MODES);
|
|
25
|
+
/** Default model ID of a key, e.g. gemini-3.1-flash-lite or claude-haiku-5-5 */
|
|
12
26
|
exports.geminiModelNameSchema = zod_1.z.string().trim().min(1).max(100);
|
|
13
27
|
// ---------- Admin (dashboard) ----------
|
|
14
28
|
exports.createGeminiApiKeySchema = zod_1.z.object({
|
|
15
29
|
name: zod_1.z.string().trim().min(1).max(255),
|
|
30
|
+
provider: exports.aiProviderSchema.default('gemini'),
|
|
16
31
|
api_key: zod_1.z.string().trim().min(20).max(512),
|
|
17
32
|
model_name: exports.geminiModelNameSchema,
|
|
18
33
|
is_active: zod_1.z.boolean().default(true),
|
|
@@ -21,6 +36,7 @@ exports.createGeminiApiKeySchema = zod_1.z.object({
|
|
|
21
36
|
exports.updateGeminiApiKeySchema = zod_1.z.object({
|
|
22
37
|
id: geminiApiKeyIdSchema,
|
|
23
38
|
name: zod_1.z.string().trim().min(1).max(255).optional(),
|
|
39
|
+
provider: exports.aiProviderSchema.optional(),
|
|
24
40
|
api_key: zod_1.z.string().trim().min(20).max(512).optional(),
|
|
25
41
|
model_name: exports.geminiModelNameSchema.optional(),
|
|
26
42
|
is_active: zod_1.z.boolean().optional(),
|
|
@@ -32,23 +48,34 @@ exports.geminiApiKeyIdBodySchema = zod_1.z.object({
|
|
|
32
48
|
exports.reorderGeminiApiKeysSchema = zod_1.z.object({
|
|
33
49
|
ids: zod_1.z.array(geminiApiKeyIdSchema).min(1),
|
|
34
50
|
});
|
|
51
|
+
exports.aiKeySettingsSchema = zod_1.z.object({
|
|
52
|
+
selection_mode: exports.aiKeySelectionModeSchema,
|
|
53
|
+
});
|
|
35
54
|
// ---------- Internal (other services) ----------
|
|
36
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Ask backend-core for the next available key. `exclude_ids` are keys that already failed for
|
|
57
|
+
* this call, so a retry moves on to the next key in the order.
|
|
58
|
+
*/
|
|
37
59
|
exports.leaseGeminiApiKeySchema = zod_1.z.object({
|
|
60
|
+
/** Only keys whose default model is this one */
|
|
38
61
|
model_name: exports.geminiModelNameSchema.optional(),
|
|
62
|
+
/** Only keys of these providers; all providers when left out */
|
|
63
|
+
providers: zod_1.z.array(exports.aiProviderSchema).min(1).optional(),
|
|
64
|
+
exclude_ids: zod_1.z.array(geminiApiKeyIdSchema).optional(),
|
|
39
65
|
});
|
|
40
66
|
exports.leasedGeminiApiKeySchema = zod_1.z.object({
|
|
41
67
|
id: zod_1.z.number().int().positive(),
|
|
68
|
+
provider: exports.aiProviderSchema,
|
|
42
69
|
api_key: zod_1.z.string(),
|
|
43
70
|
model_name: zod_1.z.string(),
|
|
44
71
|
});
|
|
45
|
-
/** Sent after every
|
|
72
|
+
/** Sent after every AI call made with a leased key. */
|
|
46
73
|
exports.reportGeminiApiKeyUsageSchema = zod_1.z.object({
|
|
47
74
|
id: geminiApiKeyIdSchema,
|
|
48
|
-
/**
|
|
75
|
+
/** Total tokens of the response (input + output); 0 when the call failed */
|
|
49
76
|
total_tokens: zod_1.z.coerce.number().int().min(0).default(0),
|
|
50
77
|
success: zod_1.z.boolean().default(true),
|
|
51
|
-
/** HTTP status
|
|
78
|
+
/** HTTP status the provider answered with, e.g. 429 */
|
|
52
79
|
error_code: zod_1.z.coerce.number().int().optional(),
|
|
53
80
|
error_message: zod_1.z.string().trim().max(2000).optional(),
|
|
54
81
|
});
|
|
@@ -16,3 +16,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./ai-conversation.schema"), exports);
|
|
18
18
|
__exportStar(require("./support-ticket.schema"), exports);
|
|
19
|
+
__exportStar(require("./offer.schema"), exports);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const offerStatusSchema: z.ZodEnum<{
|
|
3
|
+
pending: "pending";
|
|
4
|
+
accepted: "accepted";
|
|
5
|
+
countered: "countered";
|
|
6
|
+
declined: "declined";
|
|
7
|
+
}>;
|
|
8
|
+
/** PRICE_OFFER payload. Replies (accept, counter, decline) point at the offer they answer. */
|
|
9
|
+
export declare const priceOfferPayloadSchema: z.ZodObject<{
|
|
10
|
+
provider: z.ZodObject<{
|
|
11
|
+
offered_price: z.ZodCoercedNumber<unknown>;
|
|
12
|
+
currency_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
requester: z.ZodObject<{
|
|
15
|
+
offered_price: z.ZodCoercedNumber<unknown>;
|
|
16
|
+
currency_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
19
|
+
pending: "pending";
|
|
20
|
+
accepted: "accepted";
|
|
21
|
+
countered: "countered";
|
|
22
|
+
declined: "declined";
|
|
23
|
+
}>>;
|
|
24
|
+
reply_to_message_id: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
|
|
25
|
+
note: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
/** RENT_OFFER payload: prices are per period, duration counts periods. */
|
|
28
|
+
export declare const rentOfferPayloadSchema: z.ZodObject<{
|
|
29
|
+
provider: z.ZodObject<{
|
|
30
|
+
offered_price: z.ZodCoercedNumber<unknown>;
|
|
31
|
+
currency_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
requester: z.ZodObject<{
|
|
34
|
+
offered_price: z.ZodCoercedNumber<unknown>;
|
|
35
|
+
currency_id: z.ZodOptional<z.ZodCoercedNumber<unknown>>;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
38
|
+
pending: "pending";
|
|
39
|
+
accepted: "accepted";
|
|
40
|
+
countered: "countered";
|
|
41
|
+
declined: "declined";
|
|
42
|
+
}>>;
|
|
43
|
+
reply_to_message_id: z.ZodNullable<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
|
|
44
|
+
note: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
45
|
+
price_type_id: z.ZodCoercedNumber<unknown>;
|
|
46
|
+
currency_id: z.ZodCoercedNumber<unknown>;
|
|
47
|
+
duration: z.ZodCoercedNumber<unknown>;
|
|
48
|
+
start_date: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
49
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rentOfferPayloadSchema = exports.priceOfferPayloadSchema = exports.offerStatusSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
exports.offerStatusSchema = zod_1.z.enum(["pending", "accepted", "countered", "declined"]);
|
|
6
|
+
// The seller's side can be 0 when the ad has no price; the backend requires a positive buyer price
|
|
7
|
+
const offerSideSchema = zod_1.z.object({
|
|
8
|
+
offered_price: zod_1.z.coerce.number().min(0),
|
|
9
|
+
currency_id: zod_1.z.coerce.number().int().positive().optional(),
|
|
10
|
+
});
|
|
11
|
+
/** PRICE_OFFER payload. Replies (accept, counter, decline) point at the offer they answer. */
|
|
12
|
+
exports.priceOfferPayloadSchema = zod_1.z.object({
|
|
13
|
+
provider: offerSideSchema,
|
|
14
|
+
requester: offerSideSchema,
|
|
15
|
+
status: exports.offerStatusSchema.optional(),
|
|
16
|
+
reply_to_message_id: zod_1.z.coerce.number().int().positive().optional().nullable(),
|
|
17
|
+
/** Free text the sender added; the message text itself is a readable summary for older apps */
|
|
18
|
+
note: zod_1.z.string().trim().max(1000).optional().nullable(),
|
|
19
|
+
});
|
|
20
|
+
/** RENT_OFFER payload: prices are per period, duration counts periods. */
|
|
21
|
+
exports.rentOfferPayloadSchema = exports.priceOfferPayloadSchema.extend({
|
|
22
|
+
price_type_id: zod_1.z.coerce.number().int().positive(),
|
|
23
|
+
currency_id: zod_1.z.coerce.number().int().positive(),
|
|
24
|
+
duration: zod_1.z.coerce.number().int().min(1).max(1000),
|
|
25
|
+
start_date: zod_1.z
|
|
26
|
+
.string()
|
|
27
|
+
.trim()
|
|
28
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
29
|
+
.optional()
|
|
30
|
+
.nullable(),
|
|
31
|
+
});
|
|
@@ -9,6 +9,19 @@ import { IUsedProduct } from "../used-product/UsedProduct";
|
|
|
9
9
|
import { Category, Currency } from "..";
|
|
10
10
|
import { IAdminBoundary } from "../geo";
|
|
11
11
|
import type { ICompany } from "../company/Company";
|
|
12
|
+
import type { IPriceType } from "../ad-common/PriceType";
|
|
13
|
+
/** One rent price of an ad for one period (a price type linked to "For Rent", e.g. Daily). */
|
|
14
|
+
export type IAdRentPrice = {
|
|
15
|
+
id?: number;
|
|
16
|
+
ad_id?: number;
|
|
17
|
+
price_type_id: number;
|
|
18
|
+
price: number;
|
|
19
|
+
/** price converted to the base currency, used for filtering and sorting */
|
|
20
|
+
base_price?: number | null;
|
|
21
|
+
/** the price shown on cards; mirrors ads.price */
|
|
22
|
+
is_main?: boolean;
|
|
23
|
+
price_type?: IPriceType | null;
|
|
24
|
+
};
|
|
12
25
|
export type IAd = {
|
|
13
26
|
id?: number;
|
|
14
27
|
user_id?: number;
|
|
@@ -48,6 +61,10 @@ export type IAd = {
|
|
|
48
61
|
service?: IService | null;
|
|
49
62
|
product?: IUsedProduct | null;
|
|
50
63
|
adminBoundary?: IAdminBoundary | null;
|
|
64
|
+
/** Rent ads (motors and properties): one price per period */
|
|
65
|
+
rent_prices?: IAdRentPrice[] | null;
|
|
66
|
+
/** In a list filtered by rent_price_type_id: the rent price of that period */
|
|
67
|
+
matched_rent_price?: IAdRentPrice | null;
|
|
51
68
|
created_at?: string | Date;
|
|
52
69
|
updated_at?: string | Date;
|
|
53
70
|
expires_at?: string | Date | null;
|
|
@@ -75,6 +92,10 @@ export type GetAdsFilterDto = {
|
|
|
75
92
|
currency_id?: number;
|
|
76
93
|
min_price?: number;
|
|
77
94
|
max_price?: number;
|
|
95
|
+
/** Rent period (price type id): price range and price sorting then use that period's price */
|
|
96
|
+
rent_price_type_id?: number;
|
|
97
|
+
sort_by?: "price" | "created_at" | "views";
|
|
98
|
+
sort_order?: "ASC" | "DESC";
|
|
78
99
|
parent_category_id?: number;
|
|
79
100
|
property?: {
|
|
80
101
|
property_type_id?: number;
|
|
@@ -13,6 +13,9 @@ export type MetaDataType = {
|
|
|
13
13
|
maxPriceProperties: number;
|
|
14
14
|
maxPriceMotors: number;
|
|
15
15
|
maxPriceUsedProducts: number;
|
|
16
|
+
/** Highest price per rent period (price type id → base price) */
|
|
17
|
+
maxRentPricesProperties?: Record<number, number>;
|
|
18
|
+
maxRentPricesMotors?: Record<number, number>;
|
|
16
19
|
};
|
|
17
20
|
};
|
|
18
21
|
export type MetaDataDefaults = {
|
|
@@ -41,8 +41,37 @@ export type IChatMessage = {
|
|
|
41
41
|
export declare enum MessageTypeEnum {
|
|
42
42
|
TEXT = 1,
|
|
43
43
|
PRICE_OFFER = 2,
|
|
44
|
-
CONTACT_INVITATION = 3
|
|
44
|
+
CONTACT_INVITATION = 3,
|
|
45
|
+
RENT_OFFER = 4
|
|
45
46
|
}
|
|
47
|
+
/** Lifecycle of a price or rent offer. A reply is a new message pointing at the offer it answers. */
|
|
48
|
+
export declare enum OfferStatusEnum {
|
|
49
|
+
PENDING = "pending",
|
|
50
|
+
ACCEPTED = "accepted",
|
|
51
|
+
COUNTERED = "countered",
|
|
52
|
+
DECLINED = "declined"
|
|
53
|
+
}
|
|
54
|
+
export type OfferSide = {
|
|
55
|
+
offered_price: number;
|
|
56
|
+
currency_id?: number;
|
|
57
|
+
};
|
|
58
|
+
/** Payload of PRICE_OFFER messages (sale offers). status and reply_to_message_id are optional for old messages. */
|
|
59
|
+
export type PriceOfferPayload = {
|
|
60
|
+
provider: OfferSide;
|
|
61
|
+
requester: OfferSide;
|
|
62
|
+
status?: OfferStatusEnum;
|
|
63
|
+
reply_to_message_id?: number | null;
|
|
64
|
+
note?: string | null;
|
|
65
|
+
};
|
|
66
|
+
/** Payload of RENT_OFFER messages: prices are per period (price_type_id). */
|
|
67
|
+
export type RentOfferPayload = PriceOfferPayload & {
|
|
68
|
+
price_type_id: number;
|
|
69
|
+
currency_id: number;
|
|
70
|
+
/** number of periods, e.g. 3 (months) */
|
|
71
|
+
duration: number;
|
|
72
|
+
/** YYYY-MM-DD */
|
|
73
|
+
start_date?: string | null;
|
|
74
|
+
};
|
|
46
75
|
export { MessageTypeEnum as MessageTypeType };
|
|
47
76
|
export type ContactInvitationPayload = {
|
|
48
77
|
version: 1;
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MessageTypeType = exports.MessageTypeEnum = void 0;
|
|
3
|
+
exports.MessageTypeType = exports.OfferStatusEnum = exports.MessageTypeEnum = void 0;
|
|
4
4
|
var MessageTypeEnum;
|
|
5
5
|
(function (MessageTypeEnum) {
|
|
6
6
|
MessageTypeEnum[MessageTypeEnum["TEXT"] = 1] = "TEXT";
|
|
7
7
|
MessageTypeEnum[MessageTypeEnum["PRICE_OFFER"] = 2] = "PRICE_OFFER";
|
|
8
8
|
MessageTypeEnum[MessageTypeEnum["CONTACT_INVITATION"] = 3] = "CONTACT_INVITATION";
|
|
9
|
+
MessageTypeEnum[MessageTypeEnum["RENT_OFFER"] = 4] = "RENT_OFFER";
|
|
9
10
|
})(MessageTypeEnum || (exports.MessageTypeType = exports.MessageTypeEnum = MessageTypeEnum = {}));
|
|
11
|
+
/** Lifecycle of a price or rent offer. A reply is a new message pointing at the offer it answers. */
|
|
12
|
+
var OfferStatusEnum;
|
|
13
|
+
(function (OfferStatusEnum) {
|
|
14
|
+
OfferStatusEnum["PENDING"] = "pending";
|
|
15
|
+
OfferStatusEnum["ACCEPTED"] = "accepted";
|
|
16
|
+
OfferStatusEnum["COUNTERED"] = "countered";
|
|
17
|
+
OfferStatusEnum["DECLINED"] = "declined";
|
|
18
|
+
})(OfferStatusEnum || (exports.OfferStatusEnum = OfferStatusEnum = {}));
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
/** google = Gemini, anthropic = Claude */
|
|
2
|
+
export type AiProvider = 'gemini' | 'anthropic';
|
|
3
|
+
/** priority = first available key in order; balanced = least tokens used today */
|
|
4
|
+
export type AiKeySelectionMode = 'priority' | 'balanced';
|
|
5
|
+
export type IAiKeySettings = {
|
|
6
|
+
selection_mode: AiKeySelectionMode;
|
|
7
|
+
};
|
|
1
8
|
/**
|
|
2
|
-
*
|
|
9
|
+
* An AI API key (Gemini or Claude) as the dashboard sees it. The key itself never leaves backend-core in this
|
|
3
10
|
* shape: only `api_key_masked` is sent.
|
|
4
11
|
*/
|
|
5
12
|
export type IGeminiApiKey = {
|
|
6
13
|
id: number;
|
|
7
14
|
name: string;
|
|
15
|
+
provider: AiProvider;
|
|
8
16
|
/** e.g. "AIza…f3Kq" */
|
|
9
17
|
api_key_masked: string;
|
|
18
|
+
/** Default model; a service may ask for another model of the same provider */
|
|
10
19
|
model_name: string;
|
|
11
20
|
/** Dashboard order, starting at 1 */
|
|
12
21
|
sort_order: number;
|
|
@@ -17,7 +26,7 @@ export type IGeminiApiKey = {
|
|
|
17
26
|
daily_tokens: number;
|
|
18
27
|
/** The day daily_tokens belongs to (YYYY-MM-DD) */
|
|
19
28
|
daily_usage_date: string | null;
|
|
20
|
-
/** The key is skipped until this time, after
|
|
29
|
+
/** The key is skipped until this time, after the provider rejected it (quota, overload, bad key) */
|
|
21
30
|
cooldown_until: string | null;
|
|
22
31
|
last_used_at: string | null;
|
|
23
32
|
last_error: string | null;
|
package/dist/cjs/utils/index.js
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { IAd, IAdRentPrice } from "../types/ad/Ad";
|
|
2
|
+
/** The rent price shown on cards: the one marked main, else the first listed. */
|
|
3
|
+
export declare const getMainRentPrice: (ad?: Pick<IAd, "rent_prices"> | null) => IAdRentPrice | null;
|
|
4
|
+
/** The price to show for an ad in a list: the filtered period, else the main rent price. */
|
|
5
|
+
export declare const getDisplayRentPrice: (ad?: Pick<IAd, "rent_prices" | "matched_rent_price"> | null, priceTypeId?: number | null) => IAdRentPrice | null;
|
|
6
|
+
/** Rent prices ordered from the shortest period to the longest. */
|
|
7
|
+
export declare const sortRentPrices: (prices?: IAdRentPrice[] | null) => IAdRentPrice[];
|
|
8
|
+
export declare const rentOfferTotal: (pricePerPeriod: number, duration: number) => number;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rentOfferTotal = exports.sortRentPrices = exports.getDisplayRentPrice = exports.getMainRentPrice = void 0;
|
|
4
|
+
/** The rent price shown on cards: the one marked main, else the first listed. */
|
|
5
|
+
const getMainRentPrice = (ad) => {
|
|
6
|
+
var _a, _b, _c;
|
|
7
|
+
const prices = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) !== null && _a !== void 0 ? _a : [];
|
|
8
|
+
return (_c = (_b = prices.find((item) => item.is_main)) !== null && _b !== void 0 ? _b : prices[0]) !== null && _c !== void 0 ? _c : null;
|
|
9
|
+
};
|
|
10
|
+
exports.getMainRentPrice = getMainRentPrice;
|
|
11
|
+
/** The price to show for an ad in a list: the filtered period, else the main rent price. */
|
|
12
|
+
const getDisplayRentPrice = (ad, priceTypeId) => {
|
|
13
|
+
var _a;
|
|
14
|
+
if (ad === null || ad === void 0 ? void 0 : ad.matched_rent_price)
|
|
15
|
+
return ad.matched_rent_price;
|
|
16
|
+
if (priceTypeId) {
|
|
17
|
+
const match = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) === null || _a === void 0 ? void 0 : _a.find((item) => item.price_type_id === Number(priceTypeId));
|
|
18
|
+
if (match)
|
|
19
|
+
return match;
|
|
20
|
+
}
|
|
21
|
+
return (0, exports.getMainRentPrice)(ad);
|
|
22
|
+
};
|
|
23
|
+
exports.getDisplayRentPrice = getDisplayRentPrice;
|
|
24
|
+
/** Rent prices ordered from the shortest period to the longest. */
|
|
25
|
+
const sortRentPrices = (prices) => [...(prices !== null && prices !== void 0 ? prices : [])].sort((a, b) => {
|
|
26
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
27
|
+
return ((_b = (_a = a.price_type) === null || _a === void 0 ? void 0 : _a.period_days) !== null && _b !== void 0 ? _b : Number.MAX_SAFE_INTEGER) -
|
|
28
|
+
((_d = (_c = b.price_type) === null || _c === void 0 ? void 0 : _c.period_days) !== null && _d !== void 0 ? _d : Number.MAX_SAFE_INTEGER) ||
|
|
29
|
+
((_f = (_e = a.price_type) === null || _e === void 0 ? void 0 : _e.display_order) !== null && _f !== void 0 ? _f : 0) - ((_h = (_g = b.price_type) === null || _g === void 0 ? void 0 : _g.display_order) !== null && _h !== void 0 ? _h : 0);
|
|
30
|
+
});
|
|
31
|
+
exports.sortRentPrices = sortRentPrices;
|
|
32
|
+
const rentOfferTotal = (pricePerPeriod, duration) => Math.max(0, Number(pricePerPeriod) || 0) * Math.max(0, Math.floor(Number(duration) || 0));
|
|
33
|
+
exports.rentOfferTotal = rentOfferTotal;
|
|
@@ -163,6 +163,26 @@ export const adMediaSchema = z
|
|
|
163
163
|
})
|
|
164
164
|
.optional()
|
|
165
165
|
.nullable();
|
|
166
|
+
/** One rent price per period. Backend checks the period belongs to "For Rent". */
|
|
167
|
+
export const adRentPriceSchema = z.object({
|
|
168
|
+
price_type_id: z.coerce.number().int().positive(),
|
|
169
|
+
price: z.coerce.number().positive(),
|
|
170
|
+
is_main: z.coerce.boolean().optional(),
|
|
171
|
+
});
|
|
172
|
+
export const adRentPricesSchema = z
|
|
173
|
+
.array(adRentPriceSchema)
|
|
174
|
+
.max(10)
|
|
175
|
+
.superRefine((prices, ctx) => {
|
|
176
|
+
const ids = prices.map((item) => item.price_type_id);
|
|
177
|
+
if (new Set(ids).size !== ids.length) {
|
|
178
|
+
ctx.addIssue({ code: "custom", message: "Each rent period can be listed once" });
|
|
179
|
+
}
|
|
180
|
+
if (prices.filter((item) => item.is_main).length > 1) {
|
|
181
|
+
ctx.addIssue({ code: "custom", message: "Only one rent price can be the main price" });
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
.optional()
|
|
185
|
+
.nullable();
|
|
166
186
|
const baseAdCommon = z.object({
|
|
167
187
|
title: z.string().trim().optional(),
|
|
168
188
|
status_id: z.coerce.number().int().positive().optional(),
|
|
@@ -194,6 +214,7 @@ const baseAdCommon = z.object({
|
|
|
194
214
|
company_id: z.coerce.number().int().positive().optional().nullable(),
|
|
195
215
|
expires_at: z.iso.datetime().optional().nullable(),
|
|
196
216
|
adLocations: z.array(adLocationSchema).optional().nullable(),
|
|
217
|
+
rent_prices: adRentPricesSchema,
|
|
197
218
|
});
|
|
198
219
|
export const createAdSchema = z.object({
|
|
199
220
|
user_id: z.coerce.number().int().positive(),
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* The dashboard manages them through the admin schemas; other services lease a
|
|
5
|
-
* the tokens they used through the internal schemas.
|
|
3
|
+
* AI API keys (Gemini and Claude) are stored in backend-core (one table) and shared by every
|
|
4
|
+
* Deallony service. The dashboard manages them through the admin schemas; other services lease a
|
|
5
|
+
* key and report the tokens they used through the internal schemas.
|
|
6
6
|
*/
|
|
7
7
|
const geminiApiKeyIdSchema = z.coerce.number().int().positive();
|
|
8
|
-
/** Gemini
|
|
8
|
+
/** Which company's API a key belongs to: Google Gemini or Anthropic Claude */
|
|
9
|
+
export const AI_PROVIDERS = ['gemini', 'anthropic'];
|
|
10
|
+
export const aiProviderSchema = z.enum(AI_PROVIDERS);
|
|
11
|
+
/** Models offered in the dashboard for each provider; any other model ID can still be typed */
|
|
12
|
+
export const AI_PROVIDER_MODELS = {
|
|
13
|
+
gemini: ['gemini-3.5-flash-lite', 'gemini-3.1-flash-lite'],
|
|
14
|
+
anthropic: ['claude-haiku-5-5', 'claude-sonnet-5-5', 'claude-opus-5-5'],
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* How backend-core picks a key: `priority` always takes the first available key in the dashboard
|
|
18
|
+
* order, `balanced` takes the key with the fewest tokens used today.
|
|
19
|
+
*/
|
|
20
|
+
export const AI_KEY_SELECTION_MODES = ['priority', 'balanced'];
|
|
21
|
+
export const aiKeySelectionModeSchema = z.enum(AI_KEY_SELECTION_MODES);
|
|
22
|
+
/** Default model ID of a key, e.g. gemini-3.1-flash-lite or claude-haiku-5-5 */
|
|
9
23
|
export const geminiModelNameSchema = z.string().trim().min(1).max(100);
|
|
10
24
|
// ---------- Admin (dashboard) ----------
|
|
11
25
|
export const createGeminiApiKeySchema = z.object({
|
|
12
26
|
name: z.string().trim().min(1).max(255),
|
|
27
|
+
provider: aiProviderSchema.default('gemini'),
|
|
13
28
|
api_key: z.string().trim().min(20).max(512),
|
|
14
29
|
model_name: geminiModelNameSchema,
|
|
15
30
|
is_active: z.boolean().default(true),
|
|
@@ -18,6 +33,7 @@ export const createGeminiApiKeySchema = z.object({
|
|
|
18
33
|
export const updateGeminiApiKeySchema = z.object({
|
|
19
34
|
id: geminiApiKeyIdSchema,
|
|
20
35
|
name: z.string().trim().min(1).max(255).optional(),
|
|
36
|
+
provider: aiProviderSchema.optional(),
|
|
21
37
|
api_key: z.string().trim().min(20).max(512).optional(),
|
|
22
38
|
model_name: geminiModelNameSchema.optional(),
|
|
23
39
|
is_active: z.boolean().optional(),
|
|
@@ -29,23 +45,34 @@ export const geminiApiKeyIdBodySchema = z.object({
|
|
|
29
45
|
export const reorderGeminiApiKeysSchema = z.object({
|
|
30
46
|
ids: z.array(geminiApiKeyIdSchema).min(1),
|
|
31
47
|
});
|
|
48
|
+
export const aiKeySettingsSchema = z.object({
|
|
49
|
+
selection_mode: aiKeySelectionModeSchema,
|
|
50
|
+
});
|
|
32
51
|
// ---------- Internal (other services) ----------
|
|
33
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Ask backend-core for the next available key. `exclude_ids` are keys that already failed for
|
|
54
|
+
* this call, so a retry moves on to the next key in the order.
|
|
55
|
+
*/
|
|
34
56
|
export const leaseGeminiApiKeySchema = z.object({
|
|
57
|
+
/** Only keys whose default model is this one */
|
|
35
58
|
model_name: geminiModelNameSchema.optional(),
|
|
59
|
+
/** Only keys of these providers; all providers when left out */
|
|
60
|
+
providers: z.array(aiProviderSchema).min(1).optional(),
|
|
61
|
+
exclude_ids: z.array(geminiApiKeyIdSchema).optional(),
|
|
36
62
|
});
|
|
37
63
|
export const leasedGeminiApiKeySchema = z.object({
|
|
38
64
|
id: z.number().int().positive(),
|
|
65
|
+
provider: aiProviderSchema,
|
|
39
66
|
api_key: z.string(),
|
|
40
67
|
model_name: z.string(),
|
|
41
68
|
});
|
|
42
|
-
/** Sent after every
|
|
69
|
+
/** Sent after every AI call made with a leased key. */
|
|
43
70
|
export const reportGeminiApiKeyUsageSchema = z.object({
|
|
44
71
|
id: geminiApiKeyIdSchema,
|
|
45
|
-
/**
|
|
72
|
+
/** Total tokens of the response (input + output); 0 when the call failed */
|
|
46
73
|
total_tokens: z.coerce.number().int().min(0).default(0),
|
|
47
74
|
success: z.boolean().default(true),
|
|
48
|
-
/** HTTP status
|
|
75
|
+
/** HTTP status the provider answered with, e.g. 429 */
|
|
49
76
|
error_code: z.coerce.number().int().optional(),
|
|
50
77
|
error_message: z.string().trim().max(2000).optional(),
|
|
51
78
|
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const offerStatusSchema = z.enum(["pending", "accepted", "countered", "declined"]);
|
|
3
|
+
// The seller's side can be 0 when the ad has no price; the backend requires a positive buyer price
|
|
4
|
+
const offerSideSchema = z.object({
|
|
5
|
+
offered_price: z.coerce.number().min(0),
|
|
6
|
+
currency_id: z.coerce.number().int().positive().optional(),
|
|
7
|
+
});
|
|
8
|
+
/** PRICE_OFFER payload. Replies (accept, counter, decline) point at the offer they answer. */
|
|
9
|
+
export const priceOfferPayloadSchema = z.object({
|
|
10
|
+
provider: offerSideSchema,
|
|
11
|
+
requester: offerSideSchema,
|
|
12
|
+
status: offerStatusSchema.optional(),
|
|
13
|
+
reply_to_message_id: z.coerce.number().int().positive().optional().nullable(),
|
|
14
|
+
/** Free text the sender added; the message text itself is a readable summary for older apps */
|
|
15
|
+
note: z.string().trim().max(1000).optional().nullable(),
|
|
16
|
+
});
|
|
17
|
+
/** RENT_OFFER payload: prices are per period, duration counts periods. */
|
|
18
|
+
export const rentOfferPayloadSchema = priceOfferPayloadSchema.extend({
|
|
19
|
+
price_type_id: z.coerce.number().int().positive(),
|
|
20
|
+
currency_id: z.coerce.number().int().positive(),
|
|
21
|
+
duration: z.coerce.number().int().min(1).max(1000),
|
|
22
|
+
start_date: z
|
|
23
|
+
.string()
|
|
24
|
+
.trim()
|
|
25
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
26
|
+
.optional()
|
|
27
|
+
.nullable(),
|
|
28
|
+
});
|
|
@@ -3,5 +3,14 @@ export var MessageTypeEnum;
|
|
|
3
3
|
MessageTypeEnum[MessageTypeEnum["TEXT"] = 1] = "TEXT";
|
|
4
4
|
MessageTypeEnum[MessageTypeEnum["PRICE_OFFER"] = 2] = "PRICE_OFFER";
|
|
5
5
|
MessageTypeEnum[MessageTypeEnum["CONTACT_INVITATION"] = 3] = "CONTACT_INVITATION";
|
|
6
|
+
MessageTypeEnum[MessageTypeEnum["RENT_OFFER"] = 4] = "RENT_OFFER";
|
|
6
7
|
})(MessageTypeEnum || (MessageTypeEnum = {}));
|
|
8
|
+
/** Lifecycle of a price or rent offer. A reply is a new message pointing at the offer it answers. */
|
|
9
|
+
export var OfferStatusEnum;
|
|
10
|
+
(function (OfferStatusEnum) {
|
|
11
|
+
OfferStatusEnum["PENDING"] = "pending";
|
|
12
|
+
OfferStatusEnum["ACCEPTED"] = "accepted";
|
|
13
|
+
OfferStatusEnum["COUNTERED"] = "countered";
|
|
14
|
+
OfferStatusEnum["DECLINED"] = "declined";
|
|
15
|
+
})(OfferStatusEnum || (OfferStatusEnum = {}));
|
|
7
16
|
export { MessageTypeEnum as MessageTypeType };
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** The rent price shown on cards: the one marked main, else the first listed. */
|
|
2
|
+
export const getMainRentPrice = (ad) => {
|
|
3
|
+
var _a, _b, _c;
|
|
4
|
+
const prices = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) !== null && _a !== void 0 ? _a : [];
|
|
5
|
+
return (_c = (_b = prices.find((item) => item.is_main)) !== null && _b !== void 0 ? _b : prices[0]) !== null && _c !== void 0 ? _c : null;
|
|
6
|
+
};
|
|
7
|
+
/** The price to show for an ad in a list: the filtered period, else the main rent price. */
|
|
8
|
+
export const getDisplayRentPrice = (ad, priceTypeId) => {
|
|
9
|
+
var _a;
|
|
10
|
+
if (ad === null || ad === void 0 ? void 0 : ad.matched_rent_price)
|
|
11
|
+
return ad.matched_rent_price;
|
|
12
|
+
if (priceTypeId) {
|
|
13
|
+
const match = (_a = ad === null || ad === void 0 ? void 0 : ad.rent_prices) === null || _a === void 0 ? void 0 : _a.find((item) => item.price_type_id === Number(priceTypeId));
|
|
14
|
+
if (match)
|
|
15
|
+
return match;
|
|
16
|
+
}
|
|
17
|
+
return getMainRentPrice(ad);
|
|
18
|
+
};
|
|
19
|
+
/** Rent prices ordered from the shortest period to the longest. */
|
|
20
|
+
export const sortRentPrices = (prices) => [...(prices !== null && prices !== void 0 ? prices : [])].sort((a, b) => {
|
|
21
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
22
|
+
return ((_b = (_a = a.price_type) === null || _a === void 0 ? void 0 : _a.period_days) !== null && _b !== void 0 ? _b : Number.MAX_SAFE_INTEGER) -
|
|
23
|
+
((_d = (_c = b.price_type) === null || _c === void 0 ? void 0 : _c.period_days) !== null && _d !== void 0 ? _d : Number.MAX_SAFE_INTEGER) ||
|
|
24
|
+
((_f = (_e = a.price_type) === null || _e === void 0 ? void 0 : _e.display_order) !== null && _f !== void 0 ? _f : 0) - ((_h = (_g = b.price_type) === null || _g === void 0 ? void 0 : _g.display_order) !== null && _h !== void 0 ? _h : 0);
|
|
25
|
+
});
|
|
26
|
+
export const rentOfferTotal = (pricePerPeriod, duration) => Math.max(0, Number(pricePerPeriod) || 0) * Math.max(0, Math.floor(Number(duration) || 0));
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@deallony/shared",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"private": false,
|
|
5
|
-
"main": "dist/cjs/index.js",
|
|
6
|
-
"module": "dist/esm/index.js",
|
|
7
|
-
"react-native": "dist/esm/index.js",
|
|
8
|
-
"types": "dist/cjs/index.d.ts",
|
|
9
|
-
"sideEffects": false,
|
|
10
|
-
"exports": {
|
|
11
|
-
".": {
|
|
12
|
-
"require": "./dist/cjs/index.js",
|
|
13
|
-
"import": "./dist/esm/index.js",
|
|
14
|
-
"types": "./dist/cjs/index.d.ts"
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"files": [
|
|
18
|
-
"dist"
|
|
19
|
-
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"deploy": "node ./scripts/publish.js npm patch",
|
|
22
|
-
"=========": "============= UPDATE APPS LIBRARY =============",
|
|
23
|
-
"all:update": "npm run mobile:update && npm run backend:update && npm run dashboard:update && npm run whatsapp:update && npm run collect:update",
|
|
24
|
-
"mobile:update": "cd ../app && npm run upgrade:deallony:shared",
|
|
25
|
-
"backend:update": "cd ../backend-service-marketplace && pnpm run upgrade:deallony:shared",
|
|
26
|
-
"dashboard:update": "cd ../frontend-react-aloo && pnpm run upgrade:deallony:shared",
|
|
27
|
-
"whatsapp:update": "cd ../backend-nest-whatsapp-service-aloo && pnpm run upgrade:deallony:shared",
|
|
28
|
-
"collect:update": "cd ../qatayef-collect/backend && npm run upgrade:deallony:shared",
|
|
29
|
-
"current": "node -p \"require('./package.json').version\"",
|
|
30
|
-
"============================= Links with project =============================": "",
|
|
31
|
-
"link:backend": "cd ../backend-service-marketplace && pnpm add @deallony/shared@file:../shared-deallony",
|
|
32
|
-
"link:dashboard": "cd ../frontend-react-aloo && pnpm add @deallony/shared@file:../shared-deallony",
|
|
33
|
-
"link:mobile": "cd ../app && npm install ../shared-deallony",
|
|
34
|
-
"========": "============ DEFAULT ============",
|
|
35
|
-
"build": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node ./scripts/fix-esm-imports.mjs",
|
|
36
|
-
"watch": "tsc -p tsconfig.cjs.json --watch",
|
|
37
|
-
"deploy:patch": "node ./scripts/publish.js npm patch",
|
|
38
|
-
"deploy:minor": "node ./scripts/publish.js npm minor",
|
|
39
|
-
"deploy:major": "node ./scripts/publish.js npm major",
|
|
40
|
-
"deploy:dry-run": "node ./scripts/publish.js npm patch --dry-run",
|
|
41
|
-
"deploy:git": "node ./scripts/publish.js git patch",
|
|
42
|
-
"deploy:git:minor": "node ./scripts/publish.js git minor",
|
|
43
|
-
"deploy:git:major": "node ./scripts/publish.js git major",
|
|
44
|
-
"deploy:git:dry-run": "node ./scripts/publish.js git patch --dry-run",
|
|
45
|
-
"prepublishOnly": "npm run build"
|
|
46
|
-
},
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"zod": "^4.2.1"
|
|
49
|
-
},
|
|
50
|
-
"devDependencies": {
|
|
51
|
-
"@types/node": "^25.0.3",
|
|
52
|
-
"typescript": "^5.9.3"
|
|
53
|
-
},
|
|
54
|
-
"publishConfig": {
|
|
55
|
-
"registry": "https://registry.npmjs.org/",
|
|
56
|
-
"access": "public"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@deallony/shared",
|
|
3
|
+
"version": "1.2.69",
|
|
4
|
+
"private": false,
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"react-native": "dist/esm/index.js",
|
|
8
|
+
"types": "dist/cjs/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"require": "./dist/cjs/index.js",
|
|
13
|
+
"import": "./dist/esm/index.js",
|
|
14
|
+
"types": "./dist/cjs/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"deploy": "node ./scripts/publish.js npm patch",
|
|
22
|
+
"=========": "============= UPDATE APPS LIBRARY =============",
|
|
23
|
+
"all:update": "npm run mobile:update && npm run backend:update && npm run dashboard:update && npm run whatsapp:update && npm run collect:update",
|
|
24
|
+
"mobile:update": "cd ../app && npm run upgrade:deallony:shared",
|
|
25
|
+
"backend:update": "cd ../backend-service-marketplace && pnpm run upgrade:deallony:shared",
|
|
26
|
+
"dashboard:update": "cd ../frontend-react-aloo && pnpm run upgrade:deallony:shared",
|
|
27
|
+
"whatsapp:update": "cd ../backend-nest-whatsapp-service-aloo && pnpm run upgrade:deallony:shared",
|
|
28
|
+
"collect:update": "cd ../qatayef-collect/backend && npm run upgrade:deallony:shared",
|
|
29
|
+
"current": "node -p \"require('./package.json').version\"",
|
|
30
|
+
"============================= Links with project =============================": "",
|
|
31
|
+
"link:backend": "cd ../backend-service-marketplace && pnpm add @deallony/shared@file:../shared-deallony",
|
|
32
|
+
"link:dashboard": "cd ../frontend-react-aloo && pnpm add @deallony/shared@file:../shared-deallony",
|
|
33
|
+
"link:mobile": "cd ../app && npm install ../shared-deallony",
|
|
34
|
+
"========": "============ DEFAULT ============",
|
|
35
|
+
"build": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node ./scripts/fix-esm-imports.mjs",
|
|
36
|
+
"watch": "tsc -p tsconfig.cjs.json --watch",
|
|
37
|
+
"deploy:patch": "node ./scripts/publish.js npm patch",
|
|
38
|
+
"deploy:minor": "node ./scripts/publish.js npm minor",
|
|
39
|
+
"deploy:major": "node ./scripts/publish.js npm major",
|
|
40
|
+
"deploy:dry-run": "node ./scripts/publish.js npm patch --dry-run",
|
|
41
|
+
"deploy:git": "node ./scripts/publish.js git patch",
|
|
42
|
+
"deploy:git:minor": "node ./scripts/publish.js git minor",
|
|
43
|
+
"deploy:git:major": "node ./scripts/publish.js git major",
|
|
44
|
+
"deploy:git:dry-run": "node ./scripts/publish.js git patch --dry-run",
|
|
45
|
+
"prepublishOnly": "npm run build"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"zod": "^4.2.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^25.0.3",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"registry": "https://registry.npmjs.org/",
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|