Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24
Binary file added .yarn/install-state.gz
Binary file not shown.
8 changes: 8 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
approvedGitRepositories:
- "**"

enableScripts: true

nodeLinker: node-modules

npmMinimalAgeGate: 0
166 changes: 158 additions & 8 deletions data/data-migration.json
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,164 @@
}
],
"enfyra_route": [
{
"_unique": {
"path": {
"_eq": "/admin/reload"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:refresh-cw",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/reload/metadata"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:refresh-cw",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/reload/routes"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:refresh-cw",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/reload/graphql"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:refresh-cw",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/reload/guards"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:refresh-cw",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/script/validate"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:code-2",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/test/run"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:flask-conical",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/flow/trigger/:id"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:play",
"availableMethods": [
"POST"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/redis/overview"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:database-zap",
"availableMethods": [
"GET"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/redis/keys"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:key-round",
"availableMethods": [
"GET"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/redis/key"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:key-round",
"availableMethods": [
"GET",
"POST",
"DELETE"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/redis/key/ttl"
}
},
"isEnabled": true,
"isSystem": true,
"icon": "lucide:timer",
"availableMethods": [
"PATCH"
]
},
{
"_unique": {
"path": {
Expand Down Expand Up @@ -393,14 +551,6 @@
"GET"
]
},
{
"_unique": {
"path": {
"_eq": "/admin/test/run"
}
},
"isEnabled": true
},
{
"_unique": {
"path": {
Expand Down
17 changes: 16 additions & 1 deletion data/snapshot-migration.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,20 @@
"_eq": "enfyra_file"
}
},
"columnsToModify": [
{
"from": {
"name": "isPublished"
},
"to": {
"name": "isPublic",
"type": "boolean",
"isNullable": false,
"defaultValue": true,
"description": "Allow public access without authentication"
}
}
],
"relationsToModify": [
{
"from": {
Expand Down Expand Up @@ -303,7 +317,8 @@
},
{
"from": "user_definition",
"to": "enfyra_user"
"to": "enfyra_user",
"mergeKeys": ["email"]
},
{
"from": "oauth_config_definition",
Expand Down
4 changes: 2 additions & 2 deletions data/snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -555,13 +555,13 @@
"status"
],
[
"isPublished"
"isPublic"
]
],
"columns": [
{ "name": "id", "type": "uuid", "isPrimary": true, "isGenerated": true, "isNullable": false, "description": "Unique file identifier" },
{ "name": "filename", "type": "varchar", "isNullable": false, "description": "Original filename including extension" },
{ "name": "isPublished", "type": "boolean", "isNullable": false, "defaultValue": true, "description": "Allow public access without authentication" },
{ "name": "isPublic", "type": "boolean", "isNullable": false, "defaultValue": true, "description": "Allow public access without authentication" },
{ "name": "mimetype", "type": "varchar", "isNullable": false, "isGenerated": true, "description": "MIME type of the file (e.g., image/png, application/pdf)" },
{ "name": "filesize", "type": "bigint", "isNullable": false, "isGenerated": true, "description": "File size in bytes" },
{ "name": "location", "type": "varchar", "isNullable": false, "isGenerated": true, "description": "Full path or URL to the file" },
Expand Down
22 changes: 20 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
{
"name": "enfyra-server",
"version": "2.2.2",
"version": "2.2.3",
"description": "Dynamic backend platform that auto-generates REST and GraphQL APIs from database schemas",
"author": "dothinh115 <dothinh115@gmail.com>",
"private": false,
"license": "SEE LICENSE IN LICENSE",
"packageManager": "yarn@1.22.22",
"engines": {
"node": ">=24 <25",
"yarn": ">=1.22 <2"
},
"volta": {
"node": "24.9.0",
"yarn": "1.22.22"
},
"scripts": {
"check:node": "node scripts/check-node-version.js",
"preinstall": "node scripts/check-node-version.js",
"prebuild": "node scripts/check-node-version.js",
"prestart": "node scripts/check-node-version.js",
"predev": "node scripts/check-node-version.js",
"predebug": "node scripts/check-node-version.js",
"pretest:all": "node scripts/check-node-version.js",
"pretest:watch": "node scripts/check-node-version.js",
"pretest:ui": "node scripts/check-node-version.js",
"pretest:e2e": "node scripts/check-node-version.js",
"generate:mongo-fold": "tsx scripts/generate-mongo-fold-text-search.ts",
"build": "tsc && yarn generate:mongo-fold && cp -r data dist/",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
Expand Down
15 changes: 15 additions & 0 deletions scripts/check-node-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const requiredMajor = 24;
const actual = process.versions.node;
const actualMajor = Number(actual.split('.')[0]);

if (actualMajor !== requiredMajor) {
console.error(
[
`Enfyra Server requires Node ${requiredMajor}.x.`,
`Current runtime is Node ${actual} at ${process.execPath}.`,
'The server uses isolated-vm, which must match the production Node 24 runtime.',
'Switch to Node 24 before running install, dev, build, start, or tests.',
].join('\n'),
);
process.exit(1);
}
47 changes: 36 additions & 11 deletions src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,50 @@ import {
} from 'awilix';
import { EventEmitter2 } from 'eventemitter2';
import Redis from 'ioredis';
import { Queue } from 'bullmq';
import { Queue, type ConnectionOptions } from 'bullmq';
import { env } from './env';
import { SYSTEM_QUEUES } from './shared/utils/constant';

type QueueWithConnection = Queue & { __enfyraConnection?: Redis };
function buildQueueConnectionOptions(redisUri: string): ConnectionOptions {
const parsed = new URL(redisUri);
const port =
parsed.port.length > 0
? Number(parsed.port)
: parsed.protocol === 'rediss:'
? 6380
: 6379;
const dbPath = parsed.pathname.replace(/^\//, '');
const db = dbPath.length > 0 ? Number(dbPath) : undefined;
const options: any = {
host: parsed.hostname,
port,
};

if (parsed.username) {
options.username = decodeURIComponent(parsed.username);
}
if (parsed.password) {
options.password = decodeURIComponent(parsed.password);
}
if (db !== undefined && Number.isFinite(db)) {
options.db = db;
}
if (parsed.protocol === 'rediss:') {
options.tls = {};
}

return options as ConnectionOptions;
}

function createRuntimeQueue(name: string): QueueWithConnection {
const connection = new Redis(env.REDIS_URI);
const queue = new Queue(name, {
function createRuntimeQueue(name: string): Queue {
return new Queue(name, {
prefix: env.NODE_NAME,
connection,
}) as QueueWithConnection;
queue.__enfyraConnection = connection;
return queue;
connection: buildQueueConnectionOptions(env.REDIS_URI),
});
}

async function closeRuntimeQueue(queue: QueueWithConnection): Promise<void> {
async function closeRuntimeQueue(queue: Queue): Promise<void> {
await queue.close();
queue.__enfyraConnection?.disconnect();
}

import {
Expand Down
Loading