Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,785 changes: 1,713 additions & 72 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/database/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@polkadot/util": "13.5.7",
"@prosopo/common": "3.1.23",
"@prosopo/locale": "3.1.23",
"@prosopo/mongoose": "1.0.0",
"@prosopo/redis-client": "1.0.8",
"@prosopo/types": "3.6.1",
"@prosopo/types-database": "4.0.3",
Expand Down
73 changes: 10 additions & 63 deletions packages/database/src/base/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { type Logger, ProsopoDBError, getLogger } from "@prosopo/common";
import { createMongooseConnection } from "@prosopo/mongoose";
import type { IDatabase } from "@prosopo/types-database";
import { ServerApiVersion } from "mongodb";
import mongoose, { type Connection } from "mongoose";

mongoose.set("strictQuery", false);
Expand Down Expand Up @@ -98,70 +98,17 @@ export class MongoDatabase implements IDatabase {
}

// Start a new connection
this.connecting = new Promise((resolve, reject) => {
const connection = mongoose.createConnection(this.url, {
dbName: this.dbname,
serverApi: ServerApiVersion.v1,
this.connecting = (async () => {
const connection = await createMongooseConnection({
url: this.url,
dbname: this.dbname,
logger: this.logger,
});

const onConnected = () => {
this.logger.debug(() => ({
data: { mongoUrl: this.safeURL },
msg: "Database connection opened",
}));
this.connected = true;
this.connection = connection;
this.connecting = undefined;
resolve();
};

const onError = (err: unknown) => {
this.logger.error(() => ({
err,
data: { mongoUrl: this.safeURL },
msg: "Database error",
}));
this.connected = false;
this.connecting = undefined;
reject(err);
};

connection.once("open", onConnected);
connection.once("error", onError);

// Optional: handle other events
connection.on("disconnected", () => {
this.connected = false;
this.logger.debug(() => ({
data: { mongoUrl: this.safeURL },
msg: "Database disconnected",
}));
});

connection.on("reconnected", () => {
this.connected = true;
this.logger.debug(() => ({
data: { mongoUrl: this.safeURL },
msg: "Database reconnected",
}));
});

connection.on("close", () => {
this.connected = false;
this.logger.debug(() => ({
data: { mongoUrl: this.safeURL },
msg: "Database connection closed",
}));
});

connection.on("fullsetup", () => {
this.connected = true;
this.logger.debug(() => ({
data: { mongoUrl: this.safeURL },
msg: "Database connection is fully setup",
}));
});
});
this.connected = true;
this.connection = connection;
this.connecting = undefined;
})();

return this.connecting;
} catch (e) {
Expand Down
8 changes: 7 additions & 1 deletion packages/database/src/databases/captcha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import { type Logger, ProsopoDBError, getLogger } from "@prosopo/common";
import { getOrCreateModel } from "@prosopo/mongoose";
import {
type CaptchaProperties,
type ICaptchaDatabase,
Expand Down Expand Up @@ -73,7 +74,12 @@ export class CaptchaDatabase extends MongoDatabase implements ICaptchaDatabase {

CAPTCHA_TABLES.map(({ collectionName, modelName, schema }) => {
if (this.connection) {
this.tables[collectionName] = this.connection.model(modelName, schema);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.tables[collectionName] = getOrCreateModel(
this.connection,
modelName,
schema as any,
);
}
});
}
Expand Down
8 changes: 7 additions & 1 deletion packages/database/src/databases/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

import { type Logger, ProsopoDBError } from "@prosopo/common";
import { getOrCreateModel } from "@prosopo/mongoose";
import type { Timestamp } from "@prosopo/types";
import {
AccountSchema,
Expand Down Expand Up @@ -48,7 +49,12 @@ export class ClientDatabase extends MongoDatabase implements IClientDatabase {
await super.connect();
CLIENT_TABLES.map(({ collectionName, modelName, schema }) => {
if (this.connection) {
this.tables[collectionName] = this.connection.model(modelName, schema);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.tables[collectionName] = getOrCreateModel(
this.connection,
modelName,
schema as any,
);
}
});
}
Expand Down
8 changes: 7 additions & 1 deletion packages/database/src/databases/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import { isHex } from "@polkadot/util/is";
import { type Logger, ProsopoDBError } from "@prosopo/common";
import type { TranslationKey } from "@prosopo/locale";
import { getOrCreateModel } from "@prosopo/mongoose";
import {
type RedisConnection,
connectToRedis,
Expand Down Expand Up @@ -240,7 +241,12 @@ export class ProviderDatabase
const tables = {} as Tables<TableNames>;
PROVIDER_TABLES.map(({ collectionName, modelName, schema }) => {
if (this.connection) {
tables[collectionName] = this.connection.model(modelName, schema);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tables[collectionName] = getOrCreateModel(
this.connection,
modelName,
schema as any,
);
}
});
this.tables = tables;
Expand Down
15 changes: 15 additions & 0 deletions packages/mongoose/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# @prosopo/mongoose

## 1.0.0

### Major Changes

- Initial release of the Mongoose utilities package
- Added `createMongooseConnection()` for creating MongoDB connections
- Added standard middleware for automatic timestamp management (createdAt, updatedAt)
- Added version increment middleware for all mutating operations
- Added `createSchemaWithMiddleware()` for creating schemas with middleware pre-applied
- Added `getOrCreateModel()` for safe model creation that handles multiple calls
- Added `createSchemaBuilder()` for fluent schema and model creation
- Added `createModelFromZodSchema()` for Zod-to-Mongoose schema mapping with validation
- Added comprehensive test coverage
55 changes: 55 additions & 0 deletions packages/mongoose/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# @prosopo/mongoose

Mongoose utilities and middleware for Prosopo packages.

This package provides:
- Mongoose connection management utilities
- Standard middleware plugin for timestamp management (createdAt, updatedAt)
- Version increment middleware for mutation operations
- Schema and model builders with automatic middleware application via plugin
- Zod-to-Mongoose schema mapping with validation
- Model caching to support multiple `.model()` calls

## Features

### Automatic Middleware Plugin
All schemas created with this package automatically include the standard middleware plugin:
- Version increment (`__v`) on all mutating operations
- `createdAt` timestamp (set once on creation, never overwritten)
- `updatedAt` timestamp (updated on every mutation)
- Validation enabled on all update operations

The middleware is applied as a Mongoose plugin, which is the recommended approach for extending schema functionality.

### Zod Integration
Convert Zod schemas to Mongoose schemas with automatic validation in pre and post middleware.

## Usage

### Creating Schemas with newSchema()

```typescript
import { newSchema } from '@prosopo/mongoose';

// The standard middleware plugin is automatically applied
const UserSchema = newSchema({
name: { type: String, required: true },
email: { type: String, required: true }
});
```

### Using the Plugin Directly

You can also apply the standard middleware plugin to existing schemas:

```typescript
import { Schema } from 'mongoose';
import { standardMiddlewarePlugin } from '@prosopo/mongoose';

const MySchema = new Schema({
field: String
});

// Apply the plugin
MySchema.plugin(standardMiddlewarePlugin);
```
62 changes: 62 additions & 0 deletions packages/mongoose/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "@prosopo/mongoose",
"version": "1.0.0",
"author": "PROSOPO LIMITED <info@prosopo.io>",
"license": "Apache-2.0",
"private": false,
"engines": {
"node": "20",
"npm": "10.8.2"
},
"scripts": {
"clean": "del-cli --verbose dist tsconfig.tsbuildinfo",
"build": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.esm.config.ts --mode $NODE_ENV",
"build:tsc": "tsc --build --verbose",
"build:cjs": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV",
"typecheck": "tsc --project tsconfig.types.json",
"test": "NODE_ENV=${NODE_ENV:-test}; npx vitest run --config ./vite.test.config.ts"
},
"main": "dist/index.js",
"type": "module",
"exports": {
".": {
"types": "dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/cjs/index.cjs"
}
},
"types": "dist/index.d.ts",
"dependencies": {
"@prosopo/common": "3.1.21",
"mongodb": "6.15.0",
"mongoose": "8.13.0",
"zod": "3.23.8"
},
"devDependencies": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot make sure dependency and devDependency versions are the same as other packages

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit. Changed zod from 3.24.1 to 3.23.8 and added @prosopo/common as a dependency to match other packages.

"@prosopo/config": "3.1.21",
"@types/node": "22.10.2",
"@vitest/coverage-v8": "3.0.9",
"concurrently": "9.0.1",
"del-cli": "6.0.0",
"mongodb-memory-server": "10.0.0",
"npm-run-all": "4.1.5",
"tslib": "2.7.0",
"tsx": "4.20.3",
"typescript": "5.6.2",
"vite": "6.3.5",
"vitest": "3.0.9"
},
"repository": {
"type": "git",
"url": "git+https://github.com/prosopo/captcha.git"
},
"bugs": {
"url": "https://github.com/prosopo/captcha/issues"
},
"homepage": "https://github.com/prosopo/captcha#readme",
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"description": "Mongoose utilities and middleware for Prosopo packages",
"sideEffects": false
}
Loading
Loading