nitrostack 1.0.0 → 1.0.2
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/CHANGELOG.md +30 -0
- package/dist/cli/index.js +4 -1
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/src/studio/README.md +140 -0
- package/src/studio/app/api/auth/fetch-metadata/route.ts +71 -0
- package/src/studio/app/api/auth/register-client/route.ts +67 -0
- package/src/studio/app/api/chat/route.ts +123 -0
- package/src/studio/app/api/health/checks/route.ts +42 -0
- package/src/studio/app/api/health/route.ts +13 -0
- package/src/studio/app/api/init/route.ts +85 -0
- package/src/studio/app/api/ping/route.ts +13 -0
- package/src/studio/app/api/prompts/[name]/route.ts +21 -0
- package/src/studio/app/api/prompts/route.ts +13 -0
- package/src/studio/app/api/resources/[...uri]/route.ts +18 -0
- package/src/studio/app/api/resources/route.ts +13 -0
- package/src/studio/app/api/roots/route.ts +13 -0
- package/src/studio/app/api/sampling/route.ts +14 -0
- package/src/studio/app/api/tools/[name]/call/route.ts +41 -0
- package/src/studio/app/api/tools/route.ts +23 -0
- package/src/studio/app/api/widget-examples/route.ts +44 -0
- package/src/studio/app/auth/callback/page.tsx +160 -0
- package/src/studio/app/auth/page.tsx +543 -0
- package/src/studio/app/chat/page.tsx +530 -0
- package/src/studio/app/chat/page.tsx.backup +390 -0
- package/src/studio/app/globals.css +410 -0
- package/src/studio/app/health/page.tsx +177 -0
- package/src/studio/app/layout.tsx +48 -0
- package/src/studio/app/page.tsx +337 -0
- package/src/studio/app/page.tsx.backup +346 -0
- package/src/studio/app/ping/page.tsx +204 -0
- package/src/studio/app/prompts/page.tsx +228 -0
- package/src/studio/app/resources/page.tsx +313 -0
- package/src/studio/components/EnlargeModal.tsx +116 -0
- package/src/studio/components/Sidebar.tsx +133 -0
- package/src/studio/components/ToolCard.tsx +108 -0
- package/src/studio/components/WidgetRenderer.tsx +99 -0
- package/src/studio/lib/api.ts +207 -0
- package/src/studio/lib/llm-service.ts +361 -0
- package/src/studio/lib/mcp-client.ts +168 -0
- package/src/studio/lib/store.ts +192 -0
- package/src/studio/lib/theme-provider.tsx +50 -0
- package/src/studio/lib/types.ts +107 -0
- package/src/studio/lib/widget-loader.ts +90 -0
- package/src/studio/middleware.ts +27 -0
- package/src/studio/next.config.js +16 -0
- package/src/studio/package-lock.json +2696 -0
- package/src/studio/package.json +34 -0
- package/src/studio/postcss.config.mjs +10 -0
- package/src/studio/tailwind.config.ts +67 -0
- package/src/studio/tsconfig.json +41 -0
- package/templates/typescript-auth/.env.example +23 -0
- package/templates/typescript-auth/src/app.module.ts +103 -0
- package/templates/typescript-auth/src/db/database.ts +163 -0
- package/templates/typescript-auth/src/db/seed.ts +374 -0
- package/templates/typescript-auth/src/db/setup.ts +87 -0
- package/templates/typescript-auth/src/events/analytics.service.ts +52 -0
- package/templates/typescript-auth/src/events/notification.service.ts +40 -0
- package/templates/typescript-auth/src/filters/global-exception.filter.ts +28 -0
- package/templates/typescript-auth/src/guards/README.md +75 -0
- package/templates/typescript-auth/src/guards/jwt.guard.ts +105 -0
- package/templates/typescript-auth/src/health/database.health.ts +41 -0
- package/templates/typescript-auth/src/index.ts +26 -0
- package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +24 -0
- package/templates/typescript-auth/src/middleware/logging.middleware.ts +42 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +16 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +114 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +40 -0
- package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +241 -0
- package/templates/typescript-auth/src/modules/auth/auth.module.ts +16 -0
- package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +147 -0
- package/templates/typescript-auth/src/modules/auth/auth.resources.ts +84 -0
- package/templates/typescript-auth/src/modules/auth/auth.tools.ts +139 -0
- package/templates/typescript-auth/src/modules/cart/cart.module.ts +16 -0
- package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +95 -0
- package/templates/typescript-auth/src/modules/cart/cart.resources.ts +44 -0
- package/templates/typescript-auth/src/modules/cart/cart.tools.ts +281 -0
- package/templates/typescript-auth/src/modules/orders/orders.module.ts +16 -0
- package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +88 -0
- package/templates/typescript-auth/src/modules/orders/orders.resources.ts +48 -0
- package/templates/typescript-auth/src/modules/orders/orders.tools.ts +281 -0
- package/templates/typescript-auth/src/modules/products/products.module.ts +16 -0
- package/templates/typescript-auth/src/modules/products/products.prompts.ts +146 -0
- package/templates/typescript-auth/src/modules/products/products.resources.ts +98 -0
- package/templates/typescript-auth/src/modules/products/products.tools.ts +266 -0
- package/templates/typescript-auth/src/pipes/validation.pipe.ts +42 -0
- package/templates/typescript-auth/src/services/database.service.ts +90 -0
- package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +122 -0
- package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +116 -0
- package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +105 -0
- package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +139 -0
- package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +153 -0
- package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +86 -0
- package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +116 -0
- package/templates/typescript-auth/src/widgets/app/categories/page.tsx +134 -0
- package/templates/typescript-auth/src/widgets/app/layout.tsx +21 -0
- package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +129 -0
- package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +206 -0
- package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +225 -0
- package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +218 -0
- package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +121 -0
- package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +173 -0
- package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +187 -0
- package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +165 -0
- package/templates/typescript-auth/src/widgets/next.config.js +38 -0
- package/templates/typescript-auth/src/widgets/package.json +18 -0
- package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +169 -0
- package/templates/typescript-auth/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-auth/src/widgets/types/tool-data.ts +141 -0
- package/templates/typescript-auth/src/widgets/widget-manifest.json +464 -0
- package/templates/typescript-auth/tsconfig.json +27 -0
- package/templates/typescript-auth-api-key/.env +15 -0
- package/templates/typescript-auth-api-key/.env.example +4 -0
- package/templates/typescript-auth-api-key/src/app.module.ts +38 -0
- package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +47 -0
- package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +157 -0
- package/templates/typescript-auth-api-key/src/health/system.health.ts +55 -0
- package/templates/typescript-auth-api-key/src/index.ts +47 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +12 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +73 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +60 -0
- package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +71 -0
- package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +18 -0
- package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +155 -0
- package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +123 -0
- package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +133 -0
- package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +134 -0
- package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +14 -0
- package/templates/typescript-auth-api-key/src/widgets/next.config.js +37 -0
- package/templates/typescript-auth-api-key/src/widgets/package.json +24 -0
- package/templates/typescript-auth-api-key/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +48 -0
- package/templates/typescript-auth-api-key/tsconfig.json +23 -0
- package/templates/typescript-oauth/.env.example +91 -0
- package/templates/typescript-oauth/src/app.module.ts +89 -0
- package/templates/typescript-oauth/src/guards/oauth.guard.ts +127 -0
- package/templates/typescript-oauth/src/index.ts +74 -0
- package/templates/typescript-oauth/src/modules/demo/demo.module.ts +16 -0
- package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +190 -0
- package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +133 -0
- package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +134 -0
- package/templates/typescript-oauth/src/widgets/app/layout.tsx +14 -0
- package/templates/typescript-oauth/src/widgets/next.config.js +37 -0
- package/templates/typescript-oauth/src/widgets/package.json +24 -0
- package/templates/typescript-oauth/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-oauth/src/widgets/widget-manifest.json +48 -0
- package/templates/typescript-oauth/tsconfig.json +23 -0
- package/templates/typescript-starter/.env.example +4 -0
- package/templates/typescript-starter/src/app.module.ts +34 -0
- package/templates/typescript-starter/src/health/system.health.ts +55 -0
- package/templates/typescript-starter/src/index.ts +27 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.module.ts +12 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.prompts.ts +73 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +60 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +71 -0
- package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +133 -0
- package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +134 -0
- package/templates/typescript-starter/src/widgets/app/layout.tsx +14 -0
- package/templates/typescript-starter/src/widgets/next.config.js +37 -0
- package/templates/typescript-starter/src/widgets/package.json +24 -0
- package/templates/typescript-starter/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-starter/src/widgets/widget-manifest.json +48 -0
- package/templates/typescript-starter/tsconfig.json +23 -0
- package/LICENSE_URLS_UPDATE_COMPLETE.md +0 -388
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { ToolDecorator as Tool, Widget, UseGuards, z, ExecutionContext } from 'nitrostack';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { getDatabase } from '../../db/database.js';
|
|
4
|
+
import { JWTGuard } from '../../guards/jwt.guard.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Orders Tools
|
|
8
|
+
* Handles order creation and management (requires authentication)
|
|
9
|
+
*/
|
|
10
|
+
export class OrdersTools {
|
|
11
|
+
/**
|
|
12
|
+
* Create order from cart - Requires authentication
|
|
13
|
+
*/
|
|
14
|
+
@Tool({
|
|
15
|
+
name: 'create_order',
|
|
16
|
+
description: 'Create an order from the current shopping cart. Requires authentication and at least one item in cart.',
|
|
17
|
+
inputSchema: z.object({
|
|
18
|
+
payment_method: z.enum(['credit_card', 'paypal', 'apple_pay']).describe('Payment method'),
|
|
19
|
+
shipping_address_id: z.string().optional().describe('Address ID for shipping. Uses default if not provided.'),
|
|
20
|
+
}),
|
|
21
|
+
examples: {
|
|
22
|
+
request: {
|
|
23
|
+
payment_method: 'credit_card',
|
|
24
|
+
shipping_address_id: 'addr-1'
|
|
25
|
+
},
|
|
26
|
+
response: {
|
|
27
|
+
message: 'Order created successfully!',
|
|
28
|
+
order: {
|
|
29
|
+
id: 'order-12345',
|
|
30
|
+
total: 39.97,
|
|
31
|
+
status: 'pending',
|
|
32
|
+
estimated_delivery: '3-5 business days',
|
|
33
|
+
payment_method: 'credit_card',
|
|
34
|
+
created_at: '2024-01-15T10:30:00Z',
|
|
35
|
+
items: [
|
|
36
|
+
{
|
|
37
|
+
id: 'prod-1',
|
|
38
|
+
product_id: 'prod-1',
|
|
39
|
+
name: 'Essence Mascara Lash Princess',
|
|
40
|
+
quantity: 2,
|
|
41
|
+
price: 9.99,
|
|
42
|
+
image_url: 'https://cdn.dummyjson.com/product-images/beauty/essence-mascara-lash-princess/thumbnail.webp'
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'prod-2',
|
|
46
|
+
product_id: 'prod-2',
|
|
47
|
+
name: 'Eyeshadow Palette with Mirror',
|
|
48
|
+
quantity: 1,
|
|
49
|
+
price: 19.99,
|
|
50
|
+
image_url: 'https://cdn.dummyjson.com/product-images/beauty/eyeshadow-palette-with-mirror/thumbnail.webp'
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
@Widget('order-confirmation')
|
|
58
|
+
@UseGuards(JWTGuard)
|
|
59
|
+
async createOrder(input: any, context: ExecutionContext) {
|
|
60
|
+
const db = getDatabase();
|
|
61
|
+
const userId = context.auth?.subject;
|
|
62
|
+
|
|
63
|
+
// Get cart items
|
|
64
|
+
const cartItems: any[] = db.prepare(`
|
|
65
|
+
SELECT c.*, p.name, p.price, p.stock, p.image_url
|
|
66
|
+
FROM carts c
|
|
67
|
+
JOIN products p ON c.product_id = p.id
|
|
68
|
+
WHERE c.user_id = ?
|
|
69
|
+
`).all(userId);
|
|
70
|
+
|
|
71
|
+
if (cartItems.length === 0) {
|
|
72
|
+
throw new Error('Cart is empty. Add items to cart before creating an order.');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check stock availability
|
|
76
|
+
for (const item of cartItems) {
|
|
77
|
+
if (item.stock < item.quantity) {
|
|
78
|
+
throw new Error(`Insufficient stock for ${item.name}. Only ${item.stock} available.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get shipping address
|
|
83
|
+
let addressId = input.shipping_address_id;
|
|
84
|
+
if (!addressId) {
|
|
85
|
+
const defaultAddress: any = db.prepare(`
|
|
86
|
+
SELECT id FROM addresses WHERE user_id = ? AND is_default = 1
|
|
87
|
+
`).get(userId);
|
|
88
|
+
|
|
89
|
+
if (!defaultAddress) {
|
|
90
|
+
throw new Error('No shipping address found. Please add an address first.');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
addressId = defaultAddress.id;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Calculate total
|
|
97
|
+
const total = cartItems.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
|
98
|
+
|
|
99
|
+
// Create order
|
|
100
|
+
const orderId = uuidv4();
|
|
101
|
+
const now = new Date().toISOString();
|
|
102
|
+
|
|
103
|
+
db.prepare(`
|
|
104
|
+
INSERT INTO orders (id, user_id, total, status, payment_method, shipping_address_id, created_at)
|
|
105
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
106
|
+
`).run(orderId, userId, total, 'pending', input.payment_method, addressId, now);
|
|
107
|
+
|
|
108
|
+
// Create order items
|
|
109
|
+
for (const item of cartItems) {
|
|
110
|
+
const orderItemId = uuidv4();
|
|
111
|
+
db.prepare(`
|
|
112
|
+
INSERT INTO order_items (id, order_id, product_id, quantity, price)
|
|
113
|
+
VALUES (?, ?, ?, ?, ?)
|
|
114
|
+
`).run(orderItemId, orderId, item.product_id, item.quantity, item.price);
|
|
115
|
+
|
|
116
|
+
// Update product stock
|
|
117
|
+
db.prepare(`
|
|
118
|
+
UPDATE products SET stock = stock - ? WHERE id = ?
|
|
119
|
+
`).run(item.quantity, item.product_id);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Clear cart
|
|
123
|
+
db.prepare(`
|
|
124
|
+
DELETE FROM carts WHERE user_id = ?
|
|
125
|
+
`).run(userId);
|
|
126
|
+
|
|
127
|
+
context.logger.info(`Order created: ${orderId}, total=$${total.toFixed(2)}`);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
message: 'Order created successfully!',
|
|
131
|
+
order: {
|
|
132
|
+
id: orderId,
|
|
133
|
+
total,
|
|
134
|
+
status: 'pending',
|
|
135
|
+
payment_method: input.payment_method,
|
|
136
|
+
created_at: now,
|
|
137
|
+
items: cartItems.map(item => ({
|
|
138
|
+
id: item.product_id,
|
|
139
|
+
name: item.name,
|
|
140
|
+
quantity: item.quantity,
|
|
141
|
+
price: item.price,
|
|
142
|
+
image_url: item.image_url,
|
|
143
|
+
})),
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get order history - Requires authentication
|
|
150
|
+
*/
|
|
151
|
+
@Tool({
|
|
152
|
+
name: 'get_order_history',
|
|
153
|
+
description: 'Get the order history for the authenticated user. Requires authentication.',
|
|
154
|
+
inputSchema: z.object({}),
|
|
155
|
+
examples: {
|
|
156
|
+
request: {},
|
|
157
|
+
response: {
|
|
158
|
+
orders: [
|
|
159
|
+
{
|
|
160
|
+
id: 'order-12345',
|
|
161
|
+
total: 39.97,
|
|
162
|
+
status: 'pending',
|
|
163
|
+
created_at: '2024-01-15T10:30:00Z',
|
|
164
|
+
itemCount: 2
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'order-12344',
|
|
168
|
+
total: 89.96,
|
|
169
|
+
status: 'delivered',
|
|
170
|
+
created_at: '2024-01-10T14:20:00Z',
|
|
171
|
+
itemCount: 3
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
totalOrders: 2,
|
|
175
|
+
pagination: {
|
|
176
|
+
page: 1,
|
|
177
|
+
totalPages: 1,
|
|
178
|
+
totalResults: 2
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
@Widget('order-history')
|
|
184
|
+
@UseGuards(JWTGuard)
|
|
185
|
+
async getOrderHistory(input: any, context: ExecutionContext) {
|
|
186
|
+
const db = getDatabase();
|
|
187
|
+
const userId = context.auth?.subject;
|
|
188
|
+
|
|
189
|
+
const orders: any[] = db.prepare(`
|
|
190
|
+
SELECT o.*, COUNT(oi.id) as itemCount
|
|
191
|
+
FROM orders o
|
|
192
|
+
LEFT JOIN order_items oi ON o.id = oi.order_id
|
|
193
|
+
WHERE o.user_id = ?
|
|
194
|
+
GROUP BY o.id
|
|
195
|
+
ORDER BY o.created_at DESC
|
|
196
|
+
`).all(userId);
|
|
197
|
+
|
|
198
|
+
context.logger.info(`Retrieved order history: ${orders.length} orders`);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
orders,
|
|
202
|
+
totalOrders: orders.length,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get order details - Requires authentication
|
|
208
|
+
*/
|
|
209
|
+
@Tool({
|
|
210
|
+
name: 'get_order_details',
|
|
211
|
+
description: 'Get detailed information about a specific order. Requires authentication.',
|
|
212
|
+
examples: {
|
|
213
|
+
request: {
|
|
214
|
+
order_id: 'order-12345'
|
|
215
|
+
},
|
|
216
|
+
response: {
|
|
217
|
+
order: {
|
|
218
|
+
id: 'order-12345',
|
|
219
|
+
total: 39.97,
|
|
220
|
+
status: 'pending',
|
|
221
|
+
payment_method: 'credit_card',
|
|
222
|
+
created_at: '2024-01-15T10:30:00Z'
|
|
223
|
+
},
|
|
224
|
+
items: [
|
|
225
|
+
{
|
|
226
|
+
id: 'prod-1',
|
|
227
|
+
product_id: 'prod-1',
|
|
228
|
+
name: 'Essence Mascara Lash Princess',
|
|
229
|
+
quantity: 2,
|
|
230
|
+
price: 9.99,
|
|
231
|
+
image_url: 'https://cdn.dummyjson.com/product-images/beauty/essence-mascara-lash-princess/thumbnail.webp'
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: 'prod-2',
|
|
235
|
+
product_id: 'prod-2',
|
|
236
|
+
name: 'Eyeshadow Palette with Mirror',
|
|
237
|
+
quantity: 1,
|
|
238
|
+
price: 19.99,
|
|
239
|
+
image_url: 'https://cdn.dummyjson.com/product-images/beauty/eyeshadow-palette-with-mirror/thumbnail.webp'
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
inputSchema: z.object({
|
|
245
|
+
order_id: z.string().describe('The ID of the order to retrieve'),
|
|
246
|
+
}),
|
|
247
|
+
})
|
|
248
|
+
@Widget('order-details')
|
|
249
|
+
@UseGuards(JWTGuard)
|
|
250
|
+
async getOrderDetails(input: any, context: ExecutionContext) {
|
|
251
|
+
const db = getDatabase();
|
|
252
|
+
const userId = context.auth?.subject;
|
|
253
|
+
|
|
254
|
+
// Get order
|
|
255
|
+
const order: any = db.prepare(`
|
|
256
|
+
SELECT * FROM orders WHERE id = ? AND user_id = ?
|
|
257
|
+
`).get(input.order_id, userId);
|
|
258
|
+
|
|
259
|
+
if (!order) {
|
|
260
|
+
throw new Error(`Order with ID ${input.order_id} not found or you don't have access to it.`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Get order items
|
|
264
|
+
const items: any[] = db.prepare(`
|
|
265
|
+
SELECT oi.*, p.name, p.image_url
|
|
266
|
+
FROM order_items oi
|
|
267
|
+
JOIN products p ON oi.product_id = p.id
|
|
268
|
+
WHERE oi.order_id = ?
|
|
269
|
+
`).all(input.order_id);
|
|
270
|
+
|
|
271
|
+
context.logger.info(`Retrieved order details: ${input.order_id}`);
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
order: {
|
|
275
|
+
...order,
|
|
276
|
+
items,
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Module } from 'nitrostack';
|
|
2
|
+
import { ProductsTools } from './products.tools.js';
|
|
3
|
+
import { ProductsResources } from './products.resources.js';
|
|
4
|
+
import { ProductsPrompts } from './products.prompts.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Products Module
|
|
8
|
+
* Provides product browsing, search, resources, and prompts
|
|
9
|
+
*/
|
|
10
|
+
@Module({
|
|
11
|
+
name: 'products',
|
|
12
|
+
description: 'Product browsing, search, and details',
|
|
13
|
+
controllers: [ProductsTools, ProductsResources, ProductsPrompts],
|
|
14
|
+
})
|
|
15
|
+
export class ProductsModule {}
|
|
16
|
+
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { PromptDecorator as Prompt, ExecutionContext } from 'nitrostack';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Products Prompts
|
|
5
|
+
* Provides helpful prompts for product browsing and shopping
|
|
6
|
+
*/
|
|
7
|
+
export class ProductsPrompts {
|
|
8
|
+
/**
|
|
9
|
+
* Product search help prompt
|
|
10
|
+
*/
|
|
11
|
+
@Prompt({
|
|
12
|
+
name: 'product-search-help',
|
|
13
|
+
description: 'Get help with searching and browsing products',
|
|
14
|
+
arguments: [
|
|
15
|
+
{
|
|
16
|
+
name: 'looking_for',
|
|
17
|
+
description: 'What type of product you are looking for',
|
|
18
|
+
required: false,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
})
|
|
22
|
+
async productSearchHelp(args: Record<string, any>, context: ExecutionContext) {
|
|
23
|
+
const lookingFor = args.looking_for || 'general browsing';
|
|
24
|
+
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
role: 'user' as const,
|
|
28
|
+
content: `I want to find products. I'm looking for: ${lookingFor}`,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
role: 'assistant' as const,
|
|
32
|
+
content: `I'll help you find what you're looking for! Here's how to browse products:
|
|
33
|
+
|
|
34
|
+
**Available Tools:**
|
|
35
|
+
1. \`browse_products\` - Browse all products with filters
|
|
36
|
+
2. \`get_product_details\` - Get detailed info about a specific product
|
|
37
|
+
3. \`get_categories\` - See all available categories
|
|
38
|
+
|
|
39
|
+
**Browsing Products:**
|
|
40
|
+
\`\`\`
|
|
41
|
+
tool: browse_products
|
|
42
|
+
parameters:
|
|
43
|
+
category: "All" | "Electronics" | "Clothing" | "Home" | "Books" | "Sports"
|
|
44
|
+
search: "optional search term"
|
|
45
|
+
page: 1
|
|
46
|
+
limit: 10
|
|
47
|
+
\`\`\`
|
|
48
|
+
|
|
49
|
+
**Filter by Category:**
|
|
50
|
+
- Electronics - Gadgets, devices, tech
|
|
51
|
+
- Clothing - Apparel and fashion
|
|
52
|
+
- Home - Furniture and home goods
|
|
53
|
+
- Books - Reading materials
|
|
54
|
+
- Sports - Athletic equipment
|
|
55
|
+
|
|
56
|
+
**Search by Name:**
|
|
57
|
+
Add a \`search\` parameter to find specific products:
|
|
58
|
+
\`\`\`
|
|
59
|
+
browse_products({ search: "laptop", category: "Electronics" })
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
**Pagination:**
|
|
63
|
+
- \`page\`: Page number (starts at 1)
|
|
64
|
+
- \`limit\`: Items per page (1-50, default 10)
|
|
65
|
+
|
|
66
|
+
**Get Product Details:**
|
|
67
|
+
Once you find a product, use its ID to get full details:
|
|
68
|
+
\`\`\`
|
|
69
|
+
get_product_details({ product_id: "abc-123" })
|
|
70
|
+
\`\`\`
|
|
71
|
+
|
|
72
|
+
**Tips:**
|
|
73
|
+
${lookingFor.toLowerCase().includes('electronic') ? '- Check the Electronics category\n- Look for items with high stock\n- Compare prices across products' : ''}
|
|
74
|
+
${lookingFor.toLowerCase().includes('cloth') ? '- Browse the Clothing category\n- Check size availability in stock\n- Look for seasonal items' : ''}
|
|
75
|
+
- Use search to narrow down results
|
|
76
|
+
- Check stock levels before adding to cart
|
|
77
|
+
- View full details for complete specifications`,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Shopping recommendations prompt
|
|
84
|
+
*/
|
|
85
|
+
@Prompt({
|
|
86
|
+
name: 'shopping-recommendations',
|
|
87
|
+
description: 'Get personalized shopping recommendations and tips',
|
|
88
|
+
})
|
|
89
|
+
async shoppingRecommendations(args: Record<string, any>, context: ExecutionContext) {
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
role: 'user' as const,
|
|
93
|
+
content: 'Give me shopping tips and recommendations for this e-commerce store',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
role: 'assistant' as const,
|
|
97
|
+
content: `**Shopping Tips & Recommendations**
|
|
98
|
+
|
|
99
|
+
**Best Practices:**
|
|
100
|
+
1. **Check Stock First**
|
|
101
|
+
- Use \`browse_products\` to see available items
|
|
102
|
+
- Look for "stock" field in product details
|
|
103
|
+
- Low stock items go fast!
|
|
104
|
+
|
|
105
|
+
2. **Compare Products**
|
|
106
|
+
- Browse by category to see options
|
|
107
|
+
- Check prices across similar items
|
|
108
|
+
- Read full descriptions with \`get_product_details\`
|
|
109
|
+
|
|
110
|
+
3. **Smart Shopping Flow**
|
|
111
|
+
\`\`\`
|
|
112
|
+
Step 1: Browse → browse_products
|
|
113
|
+
Step 2: View Details → get_product_details
|
|
114
|
+
Step 3: Add to Cart → add_to_cart (requires login)
|
|
115
|
+
Step 4: Review Cart → view_cart
|
|
116
|
+
Step 5: Place Order → create_order
|
|
117
|
+
\`\`\`
|
|
118
|
+
|
|
119
|
+
**Categories Overview:**
|
|
120
|
+
📱 **Electronics** - Latest gadgets and tech
|
|
121
|
+
👕 **Clothing** - Fashion and apparel
|
|
122
|
+
🏠 **Home** - Furniture and decor
|
|
123
|
+
📚 **Books** - Reading materials
|
|
124
|
+
⚽ **Sports** - Athletic gear
|
|
125
|
+
|
|
126
|
+
**Money-Saving Tips:**
|
|
127
|
+
- Check all categories for deals
|
|
128
|
+
- Look for bundle opportunities
|
|
129
|
+
- Review cart before checkout
|
|
130
|
+
- Items in high stock are usually popular
|
|
131
|
+
|
|
132
|
+
**Before You Buy:**
|
|
133
|
+
✓ Verify stock availability
|
|
134
|
+
✓ Review product description
|
|
135
|
+
✓ Check your cart total
|
|
136
|
+
✓ Ensure shipping address is set
|
|
137
|
+
|
|
138
|
+
**Need Help?**
|
|
139
|
+
- Authentication issues? Use \`auth-help\` prompt
|
|
140
|
+
- Cart problems? Use \`cart-management-help\` prompt
|
|
141
|
+
- Order questions? Use \`order-help\` prompt`,
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { ResourceDecorator as Resource, ExecutionContext } from 'nitrostack';
|
|
2
|
+
import { getDatabase } from '../../db/database.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Products Resources
|
|
6
|
+
* Provides product-related resource endpoints
|
|
7
|
+
*/
|
|
8
|
+
export class ProductsResources {
|
|
9
|
+
/**
|
|
10
|
+
* Product schema resource
|
|
11
|
+
*/
|
|
12
|
+
@Resource({
|
|
13
|
+
uri: 'products://schema/product',
|
|
14
|
+
name: 'Product Schema',
|
|
15
|
+
description: 'Schema definition for product objects',
|
|
16
|
+
mimeType: 'application/json',
|
|
17
|
+
})
|
|
18
|
+
async getProductSchema(context: ExecutionContext) {
|
|
19
|
+
return {
|
|
20
|
+
type: 'json' as const,
|
|
21
|
+
data: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
id: { type: 'string', format: 'uuid', description: 'Unique product identifier' },
|
|
25
|
+
name: { type: 'string', description: 'Product name' },
|
|
26
|
+
description: { type: 'string', description: 'Product description' },
|
|
27
|
+
price: { type: 'number', minimum: 0, description: 'Price in USD' },
|
|
28
|
+
category: { type: 'string', enum: ['Electronics', 'Clothing', 'Home', 'Books', 'Sports'], description: 'Product category' },
|
|
29
|
+
stock: { type: 'integer', minimum: 0, description: 'Available stock quantity' },
|
|
30
|
+
image_url: { type: 'string', format: 'uri', nullable: true, description: 'Product image URL' },
|
|
31
|
+
created_at: { type: 'string', format: 'date-time', description: 'Product creation timestamp' },
|
|
32
|
+
},
|
|
33
|
+
required: ['id', 'name', 'description', 'price', 'category', 'stock'],
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Product categories resource
|
|
40
|
+
*/
|
|
41
|
+
@Resource({
|
|
42
|
+
uri: 'products://categories',
|
|
43
|
+
name: 'Product Categories',
|
|
44
|
+
description: 'List of all available product categories',
|
|
45
|
+
mimeType: 'application/json',
|
|
46
|
+
})
|
|
47
|
+
async getCategories(context: ExecutionContext) {
|
|
48
|
+
const db = getDatabase();
|
|
49
|
+
|
|
50
|
+
const categories: any[] = db.prepare(`
|
|
51
|
+
SELECT DISTINCT category, COUNT(*) as count
|
|
52
|
+
FROM products
|
|
53
|
+
GROUP BY category
|
|
54
|
+
ORDER BY category
|
|
55
|
+
`).all();
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
type: 'json' as const,
|
|
59
|
+
data: {
|
|
60
|
+
categories: categories.map(c => ({
|
|
61
|
+
name: c.category,
|
|
62
|
+
productCount: c.count,
|
|
63
|
+
})),
|
|
64
|
+
total: categories.length,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Product statistics resource
|
|
71
|
+
*/
|
|
72
|
+
@Resource({
|
|
73
|
+
uri: 'products://stats',
|
|
74
|
+
name: 'Product Statistics',
|
|
75
|
+
description: 'Overall product catalog statistics',
|
|
76
|
+
mimeType: 'application/json',
|
|
77
|
+
})
|
|
78
|
+
async getProductStats(context: ExecutionContext) {
|
|
79
|
+
const db = getDatabase();
|
|
80
|
+
|
|
81
|
+
const totalProducts: any = db.prepare('SELECT COUNT(*) as count FROM products').get();
|
|
82
|
+
const totalValue: any = db.prepare('SELECT SUM(price * stock) as value FROM products').get();
|
|
83
|
+
const outOfStock: any = db.prepare('SELECT COUNT(*) as count FROM products WHERE stock = 0').get();
|
|
84
|
+
const lowStock: any = db.prepare('SELECT COUNT(*) as count FROM products WHERE stock > 0 AND stock <= 5').get();
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
type: 'json' as const,
|
|
88
|
+
data: {
|
|
89
|
+
totalProducts: totalProducts.count,
|
|
90
|
+
totalInventoryValue: totalValue.value || 0,
|
|
91
|
+
outOfStock: outOfStock.count,
|
|
92
|
+
lowStock: lowStock.count,
|
|
93
|
+
categories: 5,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|