Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
57 changes: 55 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/database/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@prosopo/common": "3.1.21",
"@prosopo/config": "3.1.21",
"@prosopo/locale": "3.1.21",
"@prosopo/mongoose": "1.0.0",
"@prosopo/types": "3.5.8",
"@prosopo/types-database": "3.3.10",
"@prosopo/user-access-policy": "3.5.24",
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 @@ -49,7 +50,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 @@ -239,7 +240,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
22 changes: 22 additions & 0 deletions packages/mongoose/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# @prosopo/mongoose

Mongoose utilities and middleware for Prosopo packages.

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

## Features

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

### Zod Integration
Convert Zod schemas to Mongoose schemas with automatic validation in pre and post middleware.
61 changes: 61 additions & 0 deletions packages/mongoose/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"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": {
"mongodb": "6.15.0",
"mongoose": "8.13.0",
"zod": "3.24.1"
},
"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