@venizia/ignis-docs 0.0.8 → 0.2.0
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/README.md +7 -7
- package/content/best-practices/api-usage-examples.md +15 -12
- package/content/best-practices/architectural-patterns.md +70 -78
- package/content/best-practices/architecture-decisions.md +91 -60
- package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
- package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
- package/content/best-practices/code-style-standards/control-flow.md +5 -2
- package/content/best-practices/code-style-standards/documentation.md +13 -13
- package/content/best-practices/code-style-standards/function-patterns.md +9 -10
- package/content/best-practices/code-style-standards/index.md +1 -1
- package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
- package/content/best-practices/code-style-standards/route-definitions.md +30 -12
- package/content/best-practices/code-style-standards/tooling.md +8 -5
- package/content/best-practices/code-style-standards/type-safety.md +13 -12
- package/content/best-practices/common-pitfalls.md +56 -37
- package/content/best-practices/contribution-workflow.md +13 -14
- package/content/best-practices/data-modeling.md +46 -22
- package/content/best-practices/deployment-strategies.md +28 -27
- package/content/best-practices/error-handling.md +48 -24
- package/content/best-practices/index.md +5 -5
- package/content/best-practices/performance-optimization.md +40 -31
- package/content/best-practices/security-guidelines.md +52 -23
- package/content/best-practices/testing-strategies.md +65 -51
- package/content/best-practices/troubleshooting-tips.md +24 -24
- package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
- package/content/extensions/components/authentication/api.md +19 -19
- package/content/extensions/components/authentication/errors.md +7 -7
- package/content/extensions/components/authentication/index.md +10 -8
- package/content/extensions/components/authentication/usage.md +101 -6
- package/content/extensions/components/authorization/api.md +45 -25
- package/content/extensions/components/authorization/errors.md +6 -6
- package/content/extensions/components/authorization/index.md +11 -10
- package/content/extensions/components/authorization/usage.md +21 -21
- package/content/extensions/components/health-check.md +1 -1
- package/content/extensions/components/index.md +5 -5
- package/content/extensions/components/mail/errors.md +15 -15
- package/content/extensions/components/mail/index.md +1 -2
- package/content/extensions/components/mail/usage.md +1 -1
- package/content/extensions/components/request-tracker.md +1 -1
- package/content/extensions/components/socket-io/api.md +9 -9
- package/content/extensions/components/socket-io/errors.md +5 -5
- package/content/extensions/components/socket-io/index.md +8 -8
- package/content/extensions/components/socket-io/usage.md +1 -1
- package/content/extensions/components/static-asset/api.md +17 -4
- package/content/extensions/components/static-asset/errors.md +4 -4
- package/content/extensions/components/static-asset/index.md +26 -28
- package/content/extensions/components/static-asset/usage.md +13 -12
- package/content/extensions/components/template/index.md +2 -2
- package/content/extensions/components/template/setup-page.md +1 -1
- package/content/extensions/components/websocket/api.md +3 -3
- package/content/extensions/components/websocket/errors.md +5 -5
- package/content/extensions/components/websocket/index.md +5 -5
- package/content/extensions/components/websocket/usage.md +3 -3
- package/content/extensions/helpers/cron/index.md +2 -2
- package/content/extensions/helpers/crypto/index.md +1 -1
- package/content/extensions/helpers/env/index.md +27 -12
- package/content/extensions/helpers/error/index.md +81 -25
- package/content/extensions/helpers/index.md +2 -3
- package/content/extensions/helpers/inversion/index.md +15 -7
- package/content/extensions/helpers/kafka/compile-binary.md +92 -0
- package/content/extensions/helpers/kafka/examples.md +1 -1
- package/content/extensions/helpers/kafka/index.md +3 -0
- package/content/extensions/helpers/logger/index.md +32 -2
- package/content/extensions/helpers/network/index.md +6 -0
- package/content/extensions/helpers/queue/index.md +14 -17
- package/content/extensions/helpers/redis/index.md +548 -323
- package/content/extensions/helpers/socket-io/index.md +14 -10
- package/content/extensions/helpers/storage/api.md +44 -8
- package/content/extensions/helpers/storage/index.md +43 -7
- package/content/extensions/helpers/template/index.md +6 -3
- package/content/extensions/helpers/types/index.md +11 -8
- package/content/extensions/helpers/websocket/api.md +9 -9
- package/content/extensions/helpers/websocket/index.md +7 -7
- package/content/extensions/helpers/worker-thread/index.md +2 -2
- package/content/extensions/index.md +3 -4
- package/content/extensions/src-details/mcp-server.md +18 -24
- package/content/guides/core-concepts/application/bootstrapping.md +11 -14
- package/content/guides/core-concepts/application/index.md +3 -3
- package/content/guides/core-concepts/components.md +19 -10
- package/content/guides/core-concepts/dependency-injection.md +6 -3
- package/content/guides/core-concepts/grpc-controllers.md +6 -5
- package/content/guides/core-concepts/persistent/datasources.md +42 -43
- package/content/guides/core-concepts/persistent/index.md +16 -7
- package/content/guides/core-concepts/persistent/models.md +24 -20
- package/content/guides/core-concepts/persistent/postgres-drivers.md +201 -0
- package/content/guides/core-concepts/persistent/repositories.md +40 -23
- package/content/guides/core-concepts/persistent/search-meilisearch.md +185 -0
- package/content/guides/core-concepts/persistent/search-typesense.md +431 -0
- package/content/guides/core-concepts/persistent/transactions.md +61 -25
- package/content/guides/core-concepts/rest-controllers.md +12 -9
- package/content/guides/core-concepts/services.md +330 -60
- package/content/guides/get-started/5-minute-quickstart.md +15 -15
- package/content/guides/get-started/philosophy.md +36 -36
- package/content/guides/get-started/setup.md +3 -3
- package/content/guides/index.md +3 -3
- package/content/guides/migrations/redis-helpers-migration.md +177 -0
- package/content/guides/migrations/scoped-rbac-migration.md +17 -17
- package/content/guides/migrations/unified-connectors-migration.md +113 -0
- package/content/guides/reference/glossary.md +19 -12
- package/content/guides/reference/mcp-docs-server.md +22 -18
- package/content/guides/tutorials/building-a-crud-api.md +37 -44
- package/content/guides/tutorials/complete-installation.md +17 -17
- package/content/guides/tutorials/ecommerce-api.md +163 -124
- package/content/guides/tutorials/realtime-chat.md +181 -135
- package/content/guides/tutorials/testing.md +65 -523
- package/content/index.md +2 -180
- package/content/public/apple-touch-icon.png +0 -0
- package/content/public/og-image.png +0 -0
- package/content/public/site.webmanifest +11 -0
- package/content/references/base/application.md +4 -5
- package/content/references/base/bootstrapping.md +18 -5
- package/content/references/base/components.md +149 -120
- package/content/references/base/connectors.md +178 -0
- package/content/references/base/controllers.md +41 -30
- package/content/references/base/datasources.md +163 -92
- package/content/references/base/dependency-injection.md +34 -22
- package/content/references/base/filter-system/application-usage.md +17 -14
- package/content/references/base/filter-system/array-operators.md +7 -2
- package/content/references/base/filter-system/comparison-operators.md +3 -0
- package/content/references/base/filter-system/default-filter.md +89 -71
- package/content/references/base/filter-system/fields-order-pagination.md +22 -22
- package/content/references/base/filter-system/index.md +6 -3
- package/content/references/base/filter-system/json-filtering.md +20 -1
- package/content/references/base/filter-system/list-operators.md +1 -1
- package/content/references/base/filter-system/logical-operators.md +33 -1
- package/content/references/base/filter-system/null-operators.md +30 -1
- package/content/references/base/filter-system/quick-reference.md +23 -4
- package/content/references/base/filter-system/tips.md +5 -5
- package/content/references/base/filter-system/use-cases.md +12 -12
- package/content/references/base/grpc-controllers.md +13 -13
- package/content/references/base/index.md +24 -12
- package/content/references/base/middlewares.md +265 -327
- package/content/references/base/models.md +63 -49
- package/content/references/base/providers.md +136 -130
- package/content/references/base/repositories/advanced.md +59 -58
- package/content/references/base/repositories/index.md +115 -91
- package/content/references/base/repositories/mixins.md +55 -291
- package/content/references/base/repositories/relations.md +54 -64
- package/content/references/base/repositories/soft-deletable.md +31 -30
- package/content/references/base/services.md +296 -93
- package/content/references/configuration/environment-variables.md +49 -31
- package/content/references/configuration/index.md +6 -6
- package/content/references/index.md +17 -12
- package/content/references/quick-reference.md +65 -106
- package/content/references/utilities/crypto.md +65 -23
- package/content/references/utilities/index.md +3 -3
- package/content/references/utilities/jsx.md +6 -4
- package/content/references/utilities/module.md +68 -20
- package/content/references/utilities/parse.md +4 -14
- package/content/references/utilities/promise.md +9 -7
- package/content/references/utilities/schema.md +5 -3
- package/dist/mcp-server/common/guards.d.ts +8 -0
- package/dist/mcp-server/common/guards.d.ts.map +1 -0
- package/dist/mcp-server/common/guards.js +14 -0
- package/dist/mcp-server/common/guards.js.map +1 -0
- package/dist/mcp-server/common/index.d.ts +1 -0
- package/dist/mcp-server/common/index.d.ts.map +1 -1
- package/dist/mcp-server/common/index.js +1 -0
- package/dist/mcp-server/common/index.js.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.js +4 -2
- package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
- package/dist/mcp-server/helpers/github.helper.js +1 -1
- package/dist/mcp-server/index.js +7 -2
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/mcp-server/tools/base.tool.d.ts +6 -2
- package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/base.tool.js.map +1 -1
- package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
- package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
- package/package.json +9 -9
- package/content/extensions/helpers/testing/index.md +0 -510
- package/content/references/base/middleware.md +0 -347
|
@@ -28,7 +28,7 @@ cd ecommerce-api
|
|
|
28
28
|
bun init -y
|
|
29
29
|
|
|
30
30
|
# Install dependencies
|
|
31
|
-
bun add hono @hono/zod-openapi @venizia/ignis @venizia/ignis-helpers
|
|
31
|
+
bun add hono @hono/zod-openapi @scalar/hono-api-reference @venizia/ignis @venizia/ignis-helpers
|
|
32
32
|
bun add drizzle-orm drizzle-zod pg stripe
|
|
33
33
|
bun add -d typescript @types/bun @venizia/dev-configs drizzle-kit @types/pg
|
|
34
34
|
```
|
|
@@ -85,10 +85,11 @@ Models in IGNIS combine Drizzle ORM schemas with Entity classes.
|
|
|
85
85
|
// src/models/category.model.ts
|
|
86
86
|
import {
|
|
87
87
|
BaseEntity,
|
|
88
|
-
createRelations,
|
|
89
88
|
generateIdColumnDefs,
|
|
90
89
|
generateTzColumnDefs,
|
|
91
90
|
model,
|
|
91
|
+
RelationTypes,
|
|
92
|
+
TRelationConfig,
|
|
92
93
|
TTableObject,
|
|
93
94
|
} from '@venizia/ignis';
|
|
94
95
|
import { pgTable, text, varchar } from 'drizzle-orm/pg-core';
|
|
@@ -102,21 +103,28 @@ export const categoryTable = pgTable('Category', {
|
|
|
102
103
|
parentId: text('parent_id'),
|
|
103
104
|
});
|
|
104
105
|
|
|
105
|
-
export const categoryRelations = createRelations({
|
|
106
|
-
source: categoryTable,
|
|
107
|
-
relations: [
|
|
108
|
-
{ type: 'one', name: 'parent', target: () => categoryTable, fields: ['parentId'], references: ['id'] },
|
|
109
|
-
{ type: 'many', name: 'children', target: () => categoryTable, fields: ['id'], references: ['parentId'] },
|
|
110
|
-
],
|
|
111
|
-
});
|
|
112
|
-
|
|
113
106
|
export type TCategorySchema = typeof categoryTable;
|
|
114
107
|
export type TCategory = TTableObject<TCategorySchema>;
|
|
115
108
|
|
|
116
109
|
@model({ type: 'entity' })
|
|
117
110
|
export class Category extends BaseEntity<typeof Category.schema> {
|
|
118
111
|
static override schema = categoryTable;
|
|
119
|
-
|
|
112
|
+
|
|
113
|
+
static override relations = (): TRelationConfig[] => [
|
|
114
|
+
{
|
|
115
|
+
name: 'parent',
|
|
116
|
+
type: RelationTypes.ONE,
|
|
117
|
+
schema: categoryTable,
|
|
118
|
+
metadata: { fields: [categoryTable.parentId], references: [categoryTable.id] },
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'children',
|
|
122
|
+
type: RelationTypes.MANY,
|
|
123
|
+
schema: categoryTable,
|
|
124
|
+
metadata: { fields: [categoryTable.id], references: [categoryTable.parentId] },
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
|
|
120
128
|
static override TABLE_NAME = 'Category';
|
|
121
129
|
}
|
|
122
130
|
```
|
|
@@ -127,14 +135,15 @@ export class Category extends BaseEntity<typeof Category.schema> {
|
|
|
127
135
|
// src/models/product.model.ts
|
|
128
136
|
import {
|
|
129
137
|
BaseEntity,
|
|
130
|
-
createRelations,
|
|
131
138
|
generateIdColumnDefs,
|
|
132
139
|
generateTzColumnDefs,
|
|
133
140
|
model,
|
|
141
|
+
RelationTypes,
|
|
142
|
+
TRelationConfig,
|
|
134
143
|
TTableObject,
|
|
135
144
|
} from '@venizia/ignis';
|
|
136
145
|
import { pgTable, text, varchar, decimal, integer, boolean } from 'drizzle-orm/pg-core';
|
|
137
|
-
import { categoryTable
|
|
146
|
+
import { categoryTable } from './category.model';
|
|
138
147
|
|
|
139
148
|
export const productTable = pgTable('Product', {
|
|
140
149
|
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
@@ -150,20 +159,22 @@ export const productTable = pgTable('Product', {
|
|
|
150
159
|
imageUrl: text('image_url'),
|
|
151
160
|
});
|
|
152
161
|
|
|
153
|
-
export const productRelations = createRelations({
|
|
154
|
-
source: productTable,
|
|
155
|
-
relations: [
|
|
156
|
-
{ type: 'one', name: 'category', target: () => categoryTable, fields: ['categoryId'], references: ['id'] },
|
|
157
|
-
],
|
|
158
|
-
});
|
|
159
|
-
|
|
160
162
|
export type TProductSchema = typeof productTable;
|
|
161
163
|
export type TProduct = TTableObject<TProductSchema>;
|
|
162
164
|
|
|
163
165
|
@model({ type: 'entity' })
|
|
164
166
|
export class Product extends BaseEntity<typeof Product.schema> {
|
|
165
167
|
static override schema = productTable;
|
|
166
|
-
|
|
168
|
+
|
|
169
|
+
static override relations = (): TRelationConfig[] => [
|
|
170
|
+
{
|
|
171
|
+
name: 'category',
|
|
172
|
+
type: RelationTypes.ONE,
|
|
173
|
+
schema: categoryTable,
|
|
174
|
+
metadata: { fields: [productTable.categoryId], references: [categoryTable.id] },
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
|
|
167
178
|
static override TABLE_NAME = 'Product';
|
|
168
179
|
}
|
|
169
180
|
```
|
|
@@ -174,10 +185,11 @@ export class Product extends BaseEntity<typeof Product.schema> {
|
|
|
174
185
|
// src/models/cart.model.ts
|
|
175
186
|
import {
|
|
176
187
|
BaseEntity,
|
|
177
|
-
createRelations,
|
|
178
188
|
generateIdColumnDefs,
|
|
179
189
|
generateTzColumnDefs,
|
|
180
190
|
model,
|
|
191
|
+
RelationTypes,
|
|
192
|
+
TRelationConfig,
|
|
181
193
|
TTableObject,
|
|
182
194
|
} from '@venizia/ignis';
|
|
183
195
|
import { pgTable, text, varchar, integer } from 'drizzle-orm/pg-core';
|
|
@@ -198,21 +210,6 @@ export const cartItemTable = pgTable('CartItem', {
|
|
|
198
210
|
quantity: integer('quantity').default(1).notNull(),
|
|
199
211
|
});
|
|
200
212
|
|
|
201
|
-
export const cartRelations = createRelations({
|
|
202
|
-
source: cartTable,
|
|
203
|
-
relations: [
|
|
204
|
-
{ type: 'many', name: 'items', target: () => cartItemTable, fields: ['id'], references: ['cartId'] },
|
|
205
|
-
],
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
export const cartItemRelations = createRelations({
|
|
209
|
-
source: cartItemTable,
|
|
210
|
-
relations: [
|
|
211
|
-
{ type: 'one', name: 'cart', target: () => cartTable, fields: ['cartId'], references: ['id'] },
|
|
212
|
-
{ type: 'one', name: 'product', target: () => productTable, fields: ['productId'], references: ['id'] },
|
|
213
|
-
],
|
|
214
|
-
});
|
|
215
|
-
|
|
216
213
|
export type TCartSchema = typeof cartTable;
|
|
217
214
|
export type TCart = TTableObject<TCartSchema>;
|
|
218
215
|
export type TCartItemSchema = typeof cartItemTable;
|
|
@@ -221,14 +218,38 @@ export type TCartItem = TTableObject<TCartItemSchema>;
|
|
|
221
218
|
@model({ type: 'entity' })
|
|
222
219
|
export class Cart extends BaseEntity<typeof Cart.schema> {
|
|
223
220
|
static override schema = cartTable;
|
|
224
|
-
|
|
221
|
+
|
|
222
|
+
static override relations = (): TRelationConfig[] => [
|
|
223
|
+
{
|
|
224
|
+
name: 'items',
|
|
225
|
+
type: RelationTypes.MANY,
|
|
226
|
+
schema: cartItemTable,
|
|
227
|
+
metadata: { fields: [cartTable.id], references: [cartItemTable.cartId] },
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
|
|
225
231
|
static override TABLE_NAME = 'Cart';
|
|
226
232
|
}
|
|
227
233
|
|
|
228
234
|
@model({ type: 'entity' })
|
|
229
235
|
export class CartItem extends BaseEntity<typeof CartItem.schema> {
|
|
230
236
|
static override schema = cartItemTable;
|
|
231
|
-
|
|
237
|
+
|
|
238
|
+
static override relations = (): TRelationConfig[] => [
|
|
239
|
+
{
|
|
240
|
+
name: 'cart',
|
|
241
|
+
type: RelationTypes.ONE,
|
|
242
|
+
schema: cartTable,
|
|
243
|
+
metadata: { fields: [cartItemTable.cartId], references: [cartTable.id] },
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: 'product',
|
|
247
|
+
type: RelationTypes.ONE,
|
|
248
|
+
schema: productTable,
|
|
249
|
+
metadata: { fields: [cartItemTable.productId], references: [productTable.id] },
|
|
250
|
+
},
|
|
251
|
+
];
|
|
252
|
+
|
|
232
253
|
static override TABLE_NAME = 'CartItem';
|
|
233
254
|
}
|
|
234
255
|
```
|
|
@@ -239,10 +260,11 @@ export class CartItem extends BaseEntity<typeof CartItem.schema> {
|
|
|
239
260
|
// src/models/order.model.ts
|
|
240
261
|
import {
|
|
241
262
|
BaseEntity,
|
|
242
|
-
createRelations,
|
|
243
263
|
generateIdColumnDefs,
|
|
244
264
|
generateTzColumnDefs,
|
|
245
265
|
model,
|
|
266
|
+
RelationTypes,
|
|
267
|
+
TRelationConfig,
|
|
246
268
|
TTableObject,
|
|
247
269
|
} from '@venizia/ignis';
|
|
248
270
|
import { pgTable, text, varchar, decimal, integer, jsonb } from 'drizzle-orm/pg-core';
|
|
@@ -272,21 +294,6 @@ export const orderItemTable = pgTable('OrderItem', {
|
|
|
272
294
|
quantity: integer('quantity').notNull(),
|
|
273
295
|
});
|
|
274
296
|
|
|
275
|
-
export const orderRelations = createRelations({
|
|
276
|
-
source: orderTable,
|
|
277
|
-
relations: [
|
|
278
|
-
{ type: 'many', name: 'items', target: () => orderItemTable, fields: ['id'], references: ['orderId'] },
|
|
279
|
-
],
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
export const orderItemRelations = createRelations({
|
|
283
|
-
source: orderItemTable,
|
|
284
|
-
relations: [
|
|
285
|
-
{ type: 'one', name: 'order', target: () => orderTable, fields: ['orderId'], references: ['id'] },
|
|
286
|
-
{ type: 'one', name: 'product', target: () => productTable, fields: ['productId'], references: ['id'] },
|
|
287
|
-
],
|
|
288
|
-
});
|
|
289
|
-
|
|
290
297
|
export type TOrderSchema = typeof orderTable;
|
|
291
298
|
export type TOrder = TTableObject<TOrderSchema>;
|
|
292
299
|
export type TOrderItemSchema = typeof orderItemTable;
|
|
@@ -295,14 +302,38 @@ export type TOrderItem = TTableObject<TOrderItemSchema>;
|
|
|
295
302
|
@model({ type: 'entity' })
|
|
296
303
|
export class Order extends BaseEntity<typeof Order.schema> {
|
|
297
304
|
static override schema = orderTable;
|
|
298
|
-
|
|
305
|
+
|
|
306
|
+
static override relations = (): TRelationConfig[] => [
|
|
307
|
+
{
|
|
308
|
+
name: 'items',
|
|
309
|
+
type: RelationTypes.MANY,
|
|
310
|
+
schema: orderItemTable,
|
|
311
|
+
metadata: { fields: [orderTable.id], references: [orderItemTable.orderId] },
|
|
312
|
+
},
|
|
313
|
+
];
|
|
314
|
+
|
|
299
315
|
static override TABLE_NAME = 'Order';
|
|
300
316
|
}
|
|
301
317
|
|
|
302
318
|
@model({ type: 'entity' })
|
|
303
319
|
export class OrderItem extends BaseEntity<typeof OrderItem.schema> {
|
|
304
320
|
static override schema = orderItemTable;
|
|
305
|
-
|
|
321
|
+
|
|
322
|
+
static override relations = (): TRelationConfig[] => [
|
|
323
|
+
{
|
|
324
|
+
name: 'order',
|
|
325
|
+
type: RelationTypes.ONE,
|
|
326
|
+
schema: orderTable,
|
|
327
|
+
metadata: { fields: [orderItemTable.orderId], references: [orderTable.id] },
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: 'product',
|
|
331
|
+
type: RelationTypes.ONE,
|
|
332
|
+
schema: productTable,
|
|
333
|
+
metadata: { fields: [orderItemTable.productId], references: [productTable.id] },
|
|
334
|
+
},
|
|
335
|
+
];
|
|
336
|
+
|
|
306
337
|
static override TABLE_NAME = 'OrderItem';
|
|
307
338
|
}
|
|
308
339
|
```
|
|
@@ -326,7 +357,7 @@ import {
|
|
|
326
357
|
datasource,
|
|
327
358
|
ValueOrPromise,
|
|
328
359
|
} from '@venizia/ignis';
|
|
329
|
-
import {
|
|
360
|
+
import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
|
|
330
361
|
import { Pool } from 'pg';
|
|
331
362
|
|
|
332
363
|
interface IDSConfigs {
|
|
@@ -337,7 +368,7 @@ interface IDSConfigs {
|
|
|
337
368
|
password: string;
|
|
338
369
|
}
|
|
339
370
|
|
|
340
|
-
@datasource({ driver:
|
|
371
|
+
@datasource({ driver: NodePostgresDriver })
|
|
341
372
|
export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
|
|
342
373
|
constructor() {
|
|
343
374
|
super({
|
|
@@ -353,16 +384,18 @@ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
|
|
|
353
384
|
}
|
|
354
385
|
|
|
355
386
|
override configure(): ValueOrPromise<void> {
|
|
356
|
-
const schema = this.getSchema();
|
|
387
|
+
const schema = Object.keys(this.getSchema());
|
|
357
388
|
|
|
358
389
|
this.logger.debug(
|
|
359
390
|
'[configure] Auto-discovered schema | Schema + Relations (%s): %o',
|
|
360
|
-
|
|
361
|
-
|
|
391
|
+
schema.length,
|
|
392
|
+
schema,
|
|
362
393
|
);
|
|
363
394
|
|
|
364
|
-
|
|
365
|
-
|
|
395
|
+
// The client must land on this.client - a local would leave beginTransaction() with nothing
|
|
396
|
+
// to resolve a driver from, and it would throw `No driver and no client`. NodePostgresDriver
|
|
397
|
+
// named in @datasource above is what wires the driver and Drizzle connector from it.
|
|
398
|
+
this.client = new Pool(this.settings);
|
|
366
399
|
}
|
|
367
400
|
}
|
|
368
401
|
```
|
|
@@ -420,24 +453,25 @@ export class CartRepository extends DefaultCRUDRepository<typeof Cart.schema> {
|
|
|
420
453
|
|
|
421
454
|
async findCartItem(opts: { cartId: string; productId: string }) {
|
|
422
455
|
return this._cartItemRepo.findOne({
|
|
423
|
-
where: { cartId: opts.cartId, productId: opts.productId },
|
|
456
|
+
filter: { where: { cartId: opts.cartId, productId: opts.productId } },
|
|
424
457
|
});
|
|
425
458
|
}
|
|
426
459
|
|
|
427
460
|
async addCartItem(opts: { cartId: string; productId: string; quantity: number }) {
|
|
428
|
-
|
|
461
|
+
const rs = await this._cartItemRepo.create({ data: opts });
|
|
462
|
+
return rs.data;
|
|
429
463
|
}
|
|
430
464
|
|
|
431
465
|
async updateCartItem(opts: { itemId: string; data: { quantity: number } }) {
|
|
432
|
-
return this._cartItemRepo.updateById(opts.itemId, opts.data);
|
|
466
|
+
return this._cartItemRepo.updateById({ id: opts.itemId, data: opts.data });
|
|
433
467
|
}
|
|
434
468
|
|
|
435
469
|
async deleteCartItem(opts: { itemId: string }) {
|
|
436
|
-
return this._cartItemRepo.deleteById(opts.itemId);
|
|
470
|
+
return this._cartItemRepo.deleteById({ id: opts.itemId });
|
|
437
471
|
}
|
|
438
472
|
|
|
439
473
|
async getCartItems(opts: { cartId: string }) {
|
|
440
|
-
return this._cartItemRepo.find({ where: { cartId: opts.cartId } });
|
|
474
|
+
return this._cartItemRepo.find({ filter: { where: { cartId: opts.cartId } } });
|
|
441
475
|
}
|
|
442
476
|
|
|
443
477
|
async clearCart(opts: { cartId: string }) {
|
|
@@ -478,11 +512,12 @@ export class OrderRepository extends DefaultCRUDRepository<typeof Order.schema>
|
|
|
478
512
|
price: string;
|
|
479
513
|
quantity: number;
|
|
480
514
|
}) {
|
|
481
|
-
|
|
515
|
+
const rs = await this._orderItemRepo.create({ data: opts });
|
|
516
|
+
return rs.data;
|
|
482
517
|
}
|
|
483
518
|
|
|
484
519
|
async getOrderItems(opts: { orderId: string }) {
|
|
485
|
-
return this._orderItemRepo.find({ where: { orderId: opts.orderId } });
|
|
520
|
+
return this._orderItemRepo.find({ filter: { where: { orderId: opts.orderId } } });
|
|
486
521
|
}
|
|
487
522
|
}
|
|
488
523
|
```
|
|
@@ -496,7 +531,7 @@ import { BaseService } from '@venizia/ignis';
|
|
|
496
531
|
import { ProductRepository } from '../repositories/product.repository';
|
|
497
532
|
import { getError } from '@venizia/ignis-helpers';
|
|
498
533
|
|
|
499
|
-
@injectable()
|
|
534
|
+
@injectable({})
|
|
500
535
|
export class ProductService extends BaseService {
|
|
501
536
|
constructor(
|
|
502
537
|
@inject({ key: 'repositories.ProductRepository' })
|
|
@@ -507,18 +542,20 @@ export class ProductService extends BaseService {
|
|
|
507
542
|
|
|
508
543
|
async getActiveProducts(opts: { categoryId?: string; limit?: number; offset?: number }) {
|
|
509
544
|
return this._productRepo.find({
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
545
|
+
filter: {
|
|
546
|
+
where: {
|
|
547
|
+
isActive: true,
|
|
548
|
+
...(opts.categoryId && { categoryId: opts.categoryId }),
|
|
549
|
+
},
|
|
550
|
+
order: ['createdAt DESC'],
|
|
551
|
+
limit: opts.limit ?? 20,
|
|
552
|
+
offset: opts.offset ?? 0,
|
|
513
553
|
},
|
|
514
|
-
orderBy: { createdAt: 'desc' },
|
|
515
|
-
limit: opts.limit ?? 20,
|
|
516
|
-
offset: opts.offset ?? 0,
|
|
517
554
|
});
|
|
518
555
|
}
|
|
519
556
|
|
|
520
557
|
async getProductById(opts: { id: string }) {
|
|
521
|
-
const product = await this._productRepo.findById(opts.id);
|
|
558
|
+
const product = await this._productRepo.findById({ id: opts.id });
|
|
522
559
|
if (!product) {
|
|
523
560
|
throw getError({ statusCode: 404, message: 'Product not found' });
|
|
524
561
|
}
|
|
@@ -540,16 +577,16 @@ export class ProductService extends BaseService {
|
|
|
540
577
|
});
|
|
541
578
|
}
|
|
542
579
|
|
|
543
|
-
await this._productRepo.updateById(opts.productId, {
|
|
580
|
+
await this._productRepo.updateById({ id: opts.productId, data: {
|
|
544
581
|
stock: product.stock - opts.quantity,
|
|
545
|
-
});
|
|
582
|
+
} });
|
|
546
583
|
}
|
|
547
584
|
|
|
548
585
|
async releaseStock(opts: { productId: string; quantity: number }) {
|
|
549
586
|
const product = await this.getProductById({ id: opts.productId });
|
|
550
|
-
await this._productRepo.updateById(opts.productId, {
|
|
587
|
+
await this._productRepo.updateById({ id: opts.productId, data: {
|
|
551
588
|
stock: product.stock + opts.quantity,
|
|
552
|
-
});
|
|
589
|
+
} });
|
|
553
590
|
}
|
|
554
591
|
}
|
|
555
592
|
```
|
|
@@ -569,7 +606,7 @@ interface ICartItem {
|
|
|
569
606
|
quantity: number;
|
|
570
607
|
}
|
|
571
608
|
|
|
572
|
-
@injectable()
|
|
609
|
+
@injectable({})
|
|
573
610
|
export class CartService extends BaseService {
|
|
574
611
|
constructor(
|
|
575
612
|
@inject({ key: 'repositories.CartRepository' })
|
|
@@ -583,16 +620,14 @@ export class CartService extends BaseService {
|
|
|
583
620
|
async getOrCreateCart(opts: { userId?: string; sessionId?: string }) {
|
|
584
621
|
// Try to find existing cart
|
|
585
622
|
let cart = await this._cartRepo.findOne({
|
|
586
|
-
where: opts.userId
|
|
623
|
+
filter: { where: opts.userId
|
|
587
624
|
? { userId: opts.userId }
|
|
588
|
-
: { sessionId: opts.sessionId },
|
|
625
|
+
: { sessionId: opts.sessionId } },
|
|
589
626
|
});
|
|
590
627
|
|
|
591
628
|
if (!cart) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
sessionId: opts.sessionId,
|
|
595
|
-
});
|
|
629
|
+
const rs = await this._cartRepo.create({ data: { userId: opts.userId, sessionId: opts.sessionId } });
|
|
630
|
+
cart = rs.data;
|
|
596
631
|
}
|
|
597
632
|
|
|
598
633
|
return cart;
|
|
@@ -657,7 +692,7 @@ export class CartService extends BaseService {
|
|
|
657
692
|
}
|
|
658
693
|
|
|
659
694
|
async getCartWithItems(opts: { cartId: string }) {
|
|
660
|
-
const cart = await this._cartRepo.findById(opts.cartId);
|
|
695
|
+
const cart = await this._cartRepo.findById({ id: opts.cartId });
|
|
661
696
|
if (!cart) {
|
|
662
697
|
throw getError({ statusCode: 404, message: 'Cart not found' });
|
|
663
698
|
}
|
|
@@ -706,22 +741,24 @@ import { ProductService } from './product.service';
|
|
|
706
741
|
import { PaymentService } from './payment.service';
|
|
707
742
|
import { getError } from '@venizia/ignis-helpers';
|
|
708
743
|
|
|
744
|
+
interface IOrderAddress {
|
|
745
|
+
name: string;
|
|
746
|
+
line1: string;
|
|
747
|
+
line2?: string;
|
|
748
|
+
city: string;
|
|
749
|
+
state: string;
|
|
750
|
+
postalCode: string;
|
|
751
|
+
country: string;
|
|
752
|
+
}
|
|
753
|
+
|
|
709
754
|
interface ICreateOrderInput {
|
|
710
755
|
cartId: string;
|
|
711
756
|
email: string;
|
|
712
|
-
shippingAddress:
|
|
713
|
-
|
|
714
|
-
line1: string;
|
|
715
|
-
line2?: string;
|
|
716
|
-
city: string;
|
|
717
|
-
state: string;
|
|
718
|
-
postalCode: string;
|
|
719
|
-
country: string;
|
|
720
|
-
};
|
|
721
|
-
billingAddress?: typeof shippingAddress;
|
|
757
|
+
shippingAddress: IOrderAddress;
|
|
758
|
+
billingAddress?: IOrderAddress;
|
|
722
759
|
}
|
|
723
760
|
|
|
724
|
-
@injectable()
|
|
761
|
+
@injectable({})
|
|
725
762
|
export class OrderService extends BaseService {
|
|
726
763
|
constructor(
|
|
727
764
|
@inject({ key: 'repositories.OrderRepository' })
|
|
@@ -769,13 +806,13 @@ export class OrderService extends BaseService {
|
|
|
769
806
|
amount: Math.round(total * 100), // Stripe uses cents
|
|
770
807
|
currency: 'usd',
|
|
771
808
|
metadata: {
|
|
772
|
-
cartId: input.cartId,
|
|
773
|
-
email: input.email,
|
|
809
|
+
cartId: opts.input.cartId,
|
|
810
|
+
email: opts.input.email,
|
|
774
811
|
},
|
|
775
812
|
});
|
|
776
813
|
|
|
777
814
|
// Create order
|
|
778
|
-
const order = await this._orderRepo.create({
|
|
815
|
+
const { data: order } = await this._orderRepo.create({ data: {
|
|
779
816
|
email: opts.input.email,
|
|
780
817
|
status: 'pending_payment',
|
|
781
818
|
subtotal: subtotal.toString(),
|
|
@@ -785,7 +822,7 @@ export class OrderService extends BaseService {
|
|
|
785
822
|
shippingAddress: opts.input.shippingAddress,
|
|
786
823
|
billingAddress: opts.input.billingAddress ?? opts.input.shippingAddress,
|
|
787
824
|
paymentIntentId: paymentIntent.id,
|
|
788
|
-
});
|
|
825
|
+
} });
|
|
789
826
|
|
|
790
827
|
// Create order items
|
|
791
828
|
for (const item of cart.items) {
|
|
@@ -805,7 +842,7 @@ export class OrderService extends BaseService {
|
|
|
805
842
|
}
|
|
806
843
|
|
|
807
844
|
async confirmPayment(opts: { orderId: string; paymentIntentId: string }) {
|
|
808
|
-
const order = await this._orderRepo.findById(opts.orderId);
|
|
845
|
+
const order = await this._orderRepo.findById({ id: opts.orderId });
|
|
809
846
|
|
|
810
847
|
if (!order) {
|
|
811
848
|
throw getError({ statusCode: 404, message: 'Order not found' });
|
|
@@ -823,7 +860,7 @@ export class OrderService extends BaseService {
|
|
|
823
860
|
}
|
|
824
861
|
|
|
825
862
|
// Update order status
|
|
826
|
-
await this._orderRepo.updateById(opts.orderId, { status: 'paid' });
|
|
863
|
+
await this._orderRepo.updateById({ id: opts.orderId, data: { status: 'paid' } });
|
|
827
864
|
|
|
828
865
|
// Reserve stock for all items
|
|
829
866
|
const orderItems = await this._orderRepo.getOrderItems({ orderId: opts.orderId });
|
|
@@ -831,18 +868,20 @@ export class OrderService extends BaseService {
|
|
|
831
868
|
await this._productService.reserveStock({ productId: item.productId, quantity: item.quantity });
|
|
832
869
|
}
|
|
833
870
|
|
|
834
|
-
return this._orderRepo.findById(opts.orderId);
|
|
871
|
+
return this._orderRepo.findById({ id: opts.orderId });
|
|
835
872
|
}
|
|
836
873
|
|
|
837
874
|
async getOrdersByUser(opts: { userId: string }) {
|
|
838
875
|
return this._orderRepo.find({
|
|
839
|
-
|
|
840
|
-
|
|
876
|
+
filter: {
|
|
877
|
+
where: { userId: opts.userId },
|
|
878
|
+
order: ['createdAt DESC'],
|
|
879
|
+
},
|
|
841
880
|
});
|
|
842
881
|
}
|
|
843
882
|
|
|
844
883
|
async getOrderById(opts: { orderId: string }) {
|
|
845
|
-
const order = await this._orderRepo.findById(opts.orderId);
|
|
884
|
+
const order = await this._orderRepo.findById({ id: opts.orderId });
|
|
846
885
|
if (!order) {
|
|
847
886
|
throw getError({ statusCode: 404, message: 'Order not found' });
|
|
848
887
|
}
|
|
@@ -858,7 +897,7 @@ export class OrderService extends BaseService {
|
|
|
858
897
|
throw getError({ statusCode: 400, message: 'Invalid status' });
|
|
859
898
|
}
|
|
860
899
|
|
|
861
|
-
return this._orderRepo.updateById(opts.orderId, { status: opts.status });
|
|
900
|
+
return this._orderRepo.updateById({ id: opts.orderId, data: { status: opts.status } });
|
|
862
901
|
}
|
|
863
902
|
}
|
|
864
903
|
```
|
|
@@ -870,16 +909,16 @@ export class OrderService extends BaseService {
|
|
|
870
909
|
import { injectable } from '@venizia/ignis';
|
|
871
910
|
import { BaseService } from '@venizia/ignis';
|
|
872
911
|
import Stripe from 'stripe';
|
|
873
|
-
import {
|
|
912
|
+
import { applicationEnvironment } from '@venizia/ignis-helpers';
|
|
874
913
|
|
|
875
|
-
@injectable()
|
|
914
|
+
@injectable({})
|
|
876
915
|
export class PaymentService extends BaseService {
|
|
877
916
|
private _stripe: Stripe;
|
|
878
917
|
|
|
879
918
|
constructor() {
|
|
880
919
|
super({ scope: PaymentService.name });
|
|
881
920
|
|
|
882
|
-
const secretKey =
|
|
921
|
+
const secretKey = applicationEnvironment.get<string>('APP_ENV_STRIPE_SECRET_KEY');
|
|
883
922
|
this._stripe = new Stripe(secretKey, {
|
|
884
923
|
apiVersion: '2023-10-16',
|
|
885
924
|
});
|
|
@@ -913,7 +952,7 @@ export class PaymentService extends BaseService {
|
|
|
913
952
|
}
|
|
914
953
|
|
|
915
954
|
async createWebhookEvent(opts: { payload: string; signature: string }) {
|
|
916
|
-
const webhookSecret =
|
|
955
|
+
const webhookSecret = applicationEnvironment.get<string>('APP_ENV_STRIPE_WEBHOOK_SECRET');
|
|
917
956
|
return this._stripe.webhooks.constructEvent(opts.payload, opts.signature, webhookSecret);
|
|
918
957
|
}
|
|
919
958
|
}
|
|
@@ -961,7 +1000,7 @@ const ProductRoutes = {
|
|
|
961
1000
|
},
|
|
962
1001
|
GET_BY_ID: {
|
|
963
1002
|
method: HTTP.Methods.GET,
|
|
964
|
-
path: '
|
|
1003
|
+
path: '/{id}',
|
|
965
1004
|
request: {
|
|
966
1005
|
params: z.object({ id: z.string().uuid() }),
|
|
967
1006
|
},
|
|
@@ -1059,7 +1098,7 @@ const CartRoutes = {
|
|
|
1059
1098
|
},
|
|
1060
1099
|
UPDATE_ITEM: {
|
|
1061
1100
|
method: HTTP.Methods.PUT,
|
|
1062
|
-
path: '/items
|
|
1101
|
+
path: '/items/{productId}',
|
|
1063
1102
|
request: {
|
|
1064
1103
|
params: z.object({ productId: z.string().uuid() }),
|
|
1065
1104
|
body: jsonContent({
|
|
@@ -1077,7 +1116,7 @@ const CartRoutes = {
|
|
|
1077
1116
|
},
|
|
1078
1117
|
REMOVE_ITEM: {
|
|
1079
1118
|
method: HTTP.Methods.DELETE,
|
|
1080
|
-
path: '/items
|
|
1119
|
+
path: '/items/{productId}',
|
|
1081
1120
|
request: {
|
|
1082
1121
|
params: z.object({ productId: z.string().uuid() }),
|
|
1083
1122
|
},
|
|
@@ -1198,7 +1237,7 @@ const OrderRoutes = {
|
|
|
1198
1237
|
},
|
|
1199
1238
|
CONFIRM: {
|
|
1200
1239
|
method: HTTP.Methods.POST,
|
|
1201
|
-
path: '
|
|
1240
|
+
path: '/{id}/confirm',
|
|
1202
1241
|
request: {
|
|
1203
1242
|
params: z.object({ id: z.string().uuid() }),
|
|
1204
1243
|
body: jsonContent({
|
|
@@ -1216,7 +1255,7 @@ const OrderRoutes = {
|
|
|
1216
1255
|
},
|
|
1217
1256
|
GET_BY_ID: {
|
|
1218
1257
|
method: HTTP.Methods.GET,
|
|
1219
|
-
path: '
|
|
1258
|
+
path: '/{id}',
|
|
1220
1259
|
request: {
|
|
1221
1260
|
params: z.object({ id: z.string().uuid() }),
|
|
1222
1261
|
},
|
|
@@ -1272,7 +1311,7 @@ export class OrderController extends BaseRestController {
|
|
|
1272
1311
|
```typescript
|
|
1273
1312
|
// src/application.ts
|
|
1274
1313
|
import { BaseApplication, IApplicationInfo } from '@venizia/ignis';
|
|
1275
|
-
import { HealthCheckComponent,
|
|
1314
|
+
import { HealthCheckComponent, ApiReferenceComponent } from '@venizia/ignis';
|
|
1276
1315
|
|
|
1277
1316
|
import { ProductController } from './controllers/product';
|
|
1278
1317
|
import { CartController } from './controllers/cart';
|
|
@@ -1318,7 +1357,7 @@ export class EcommerceApp extends BaseApplication {
|
|
|
1318
1357
|
|
|
1319
1358
|
// Components
|
|
1320
1359
|
this.component(HealthCheckComponent);
|
|
1321
|
-
this.component(
|
|
1360
|
+
this.component(ApiReferenceComponent);
|
|
1322
1361
|
}
|
|
1323
1362
|
|
|
1324
1363
|
postConfigure() {}
|