@base44-preview/cli 0.0.50-pr.480.f46dcd8 → 0.0.50-pr.481.3fd0022

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/dist/cli/index.js CHANGED
@@ -219982,7 +219982,8 @@ var theme = {
219982
219982
  bold: source_default.bold,
219983
219983
  dim: source_default.dim,
219984
219984
  error: source_default.red,
219985
- warn: source_default.yellow
219985
+ warn: source_default.yellow,
219986
+ info: source_default.cyan
219986
219987
  },
219987
219988
  format: {
219988
219989
  errorContext(ctx) {
@@ -235097,8 +235098,8 @@ var TOKEN_REFRESH_BUFFER_MS = 60 * 1000;
235097
235098
  var refreshPromise = null;
235098
235099
  async function readAuth() {
235099
235100
  try {
235100
- const parsed = await readJsonFile(getAuthFilePath());
235101
- const result = AuthDataSchema.safeParse(parsed);
235101
+ const authData = await readJsonFile(getAuthFilePath());
235102
+ const result = AuthDataSchema.safeParse(authData);
235102
235103
  if (!result.success) {
235103
235104
  throw new SchemaValidationError("Invalid authentication data", result.error, getAuthFilePath());
235104
235105
  }
@@ -241594,30 +241595,12 @@ var ToolConfigSchema = exports_external.union([
241594
241595
  EntityToolConfigSchema,
241595
241596
  BackendFunctionToolConfigSchema
241596
241597
  ]);
241597
- var EntityAccessRuleSchema = exports_external.union([
241598
- exports_external.boolean(),
241599
- exports_external.record(exports_external.string(), exports_external.unknown())
241600
- ]);
241601
- var FunctionAccessEntrySchema = exports_external.looseObject({
241602
- name: exports_external.string().min(1)
241603
- });
241604
- var AgentAccessConfigSchema = exports_external.object({
241605
- entities: exports_external.record(exports_external.string(), exports_external.record(exports_external.string(), EntityAccessRuleSchema)).optional().default({}),
241606
- functions: exports_external.array(FunctionAccessEntrySchema).optional().default([])
241607
- });
241608
- var CodeModeConfigSchema = exports_external.object({
241609
- access: AgentAccessConfigSchema.optional().default({
241610
- entities: {},
241611
- functions: []
241612
- })
241613
- });
241614
241598
  var AgentConfigSchema = exports_external.looseObject({
241615
241599
  name: exports_external.string().trim().min(1).max(100),
241616
241600
  description: exports_external.string().trim().min(1, "Description is required"),
241617
241601
  instructions: exports_external.string().trim().min(1, "Instructions are required"),
241618
241602
  tool_configs: exports_external.array(ToolConfigSchema).optional().default([]),
241619
- whatsapp_greeting: exports_external.string().nullable().optional(),
241620
- code_mode: CodeModeConfigSchema.optional()
241603
+ whatsapp_greeting: exports_external.string().nullable().optional()
241621
241604
  });
241622
241605
  var SyncAgentsResponseSchema = exports_external.object({
241623
241606
  created: exports_external.array(exports_external.string()),
@@ -243425,10 +243408,12 @@ var package_default = {
243425
243408
  "@types/ejs": "^3.1.5",
243426
243409
  "@types/express": "^5.0.6",
243427
243410
  "@types/json-schema": "^7.0.15",
243411
+ "@types/jsonwebtoken": "^9.0.10",
243428
243412
  "@types/lodash": "^4.17.24",
243429
243413
  "@types/multer": "^2.0.0",
243430
243414
  "@types/node": "^22.10.5",
243431
243415
  "@vercel/detect-agent": "^1.1.0",
243416
+ outdent: "^0.8.0",
243432
243417
  chalk: "^5.6.2",
243433
243418
  chokidar: "^5.0.0",
243434
243419
  commander: "^12.1.0",
@@ -243443,6 +243428,7 @@ var package_default = {
243443
243428
  globby: "^16.1.0",
243444
243429
  "http-proxy-middleware": "^3.0.5",
243445
243430
  "json-schema-to-typescript": "^15.0.4",
243431
+ jsonwebtoken: "^9.0.3",
243446
243432
  json5: "^2.2.3",
243447
243433
  ky: "^1.14.2",
243448
243434
  lodash: "^4.17.23",
@@ -243458,6 +243444,7 @@ var package_default = {
243458
243444
  typescript: "^5.7.2",
243459
243445
  vitest: "^4.0.16",
243460
243446
  yaml: "^2.8.2",
243447
+ qs: "^6.12.3",
243461
243448
  zod: "^4.3.5"
243462
243449
  },
243463
243450
  engines: {
@@ -253070,9 +253057,12 @@ function getTypesCommand() {
253070
253057
  return new Command("types").description("Manage TypeScript type generation").addCommand(getTypesGenerateCommand());
253071
253058
  }
253072
253059
 
253060
+ // src/cli/commands/dev.ts
253061
+ import process21 from "node:process";
253062
+
253073
253063
  // src/cli/dev/dev-server/main.ts
253074
253064
  var import_cors = __toESM(require_lib4(), 1);
253075
- var import_express5 = __toESM(require_express(), 1);
253065
+ var import_express6 = __toESM(require_express(), 1);
253076
253066
  import { dirname as dirname17, join as join24 } from "node:path";
253077
253067
 
253078
253068
  // ../../node_modules/get-port/index.js
@@ -253636,7 +253626,9 @@ class Validator {
253636
253626
  }
253637
253627
 
253638
253628
  // src/cli/dev/dev-server/db/database.ts
253629
+ var PRIVATE_COLLECTION_PREFIX = "$";
253639
253630
  var USER_COLLECTION = "user";
253631
+ var PRIVATE_USER_COLLECTION = PRIVATE_COLLECTION_PREFIX + USER_COLLECTION;
253640
253632
 
253641
253633
  class Database {
253642
253634
  collections = new Map;
@@ -253658,6 +253650,7 @@ class Database {
253658
253650
  this.schemas.set(USER_COLLECTION, this.buildUserSchema(userEntity));
253659
253651
  const collection = new import_nedb.default;
253660
253652
  this.collections.set(USER_COLLECTION, collection);
253653
+ this.collections.set(PRIVATE_USER_COLLECTION, new import_nedb.default);
253661
253654
  const userInfo = await readAuth();
253662
253655
  const now = getNowISOTimestamp();
253663
253656
  await collection.insertAsync({
@@ -253698,8 +253691,13 @@ class Database {
253698
253691
  getCollection(name2) {
253699
253692
  return this.collections.get(this.normalizeName(name2));
253700
253693
  }
253694
+ getSchema(entityName) {
253695
+ return this.schemas.get(this.normalizeName(entityName));
253696
+ }
253701
253697
  getCollectionNames() {
253702
- return Array.from(this.collections.keys());
253698
+ return Array.from(this.collections.keys()).filter((name2) => {
253699
+ return !name2.startsWith(PRIVATE_COLLECTION_PREFIX);
253700
+ });
253703
253701
  }
253704
253702
  dropAll() {
253705
253703
  for (const collection of this.collections.values()) {
@@ -253763,15 +253761,347 @@ function broadcastEntityEvent(io6, appId, entityName, event) {
253763
253761
  });
253764
253762
  }
253765
253763
 
253766
- // src/cli/dev/dev-server/routes/entities/entities-router.ts
253767
- var import_express3 = __toESM(require_express(), 1);
253768
-
253769
- // src/cli/dev/dev-server/routes/entities/entities-user-router.ts
253764
+ // src/cli/dev/dev-server/routes/auth-router.ts
253770
253765
  var import_express2 = __toESM(require_express(), 1);
253771
253766
  var import_jsonwebtoken = __toESM(require_jsonwebtoken(), 1);
253772
- function createUserRouter(db2, logger2) {
253767
+ import { randomInt } from "node:crypto";
253768
+ var LOCAL_DEV_SECRET = "LOCAL_DEV_SECRET";
253769
+ var TEN_MINUTES = 10 * 60 * 1000;
253770
+ var generateCode = () => {
253771
+ return randomInt(1e5, 1e6).toString();
253772
+ };
253773
+ var createJwtToken = (email3) => {
253774
+ return import_jsonwebtoken.default.sign({ sub: email3 }, LOCAL_DEV_SECRET, {
253775
+ expiresIn: "360d"
253776
+ });
253777
+ };
253778
+ var LoginBody = object({ email: email2(), password: string2() });
253779
+ var VerifyOtpBody = object({ email: email2(), otp_code: string2() });
253780
+ function createAuthRouter(db2, logger2) {
253773
253781
  const router = import_express2.Router({ mergeParams: true });
253774
253782
  const parseBody = import_express2.json();
253783
+ router.post("/login", parseBody, async (req, res) => {
253784
+ const { email: email3, password } = LoginBody.parse(req.body);
253785
+ const result = await db2.getCollection(USER_COLLECTION)?.findOneAsync({ email: email3 });
253786
+ if (result) {
253787
+ const privateUserData = await db2.getCollection(PRIVATE_USER_COLLECTION)?.findOneAsync({ email: email3 });
253788
+ if (result.role === "admin" || privateUserData?.password === password) {
253789
+ res.json({
253790
+ access_token: createJwtToken(email3),
253791
+ success: true,
253792
+ user: {}
253793
+ });
253794
+ } else {
253795
+ res.status(400).json({
253796
+ detail: "Invalid email or password",
253797
+ error_type: "HTTPException",
253798
+ message: "Invalid email or password",
253799
+ request_id: null,
253800
+ traceback: ""
253801
+ });
253802
+ }
253803
+ return;
253804
+ }
253805
+ res.status(401).json({ error: "Unauthorized" });
253806
+ });
253807
+ router.post("/register", parseBody, async (req, res) => {
253808
+ const { email: email3, password } = LoginBody.parse(req.body);
253809
+ if ((password || "").length < 8) {
253810
+ res.status(400).json({
253811
+ detail: "Password must be at least 8 characters long",
253812
+ error_type: "HTTPException",
253813
+ message: "Password must be at least 8 characters long",
253814
+ request_id: null,
253815
+ traceback: ""
253816
+ });
253817
+ return;
253818
+ }
253819
+ const result = await db2.getCollection(USER_COLLECTION)?.findOneAsync({ email: email3 });
253820
+ if (result) {
253821
+ res.status(400).json({
253822
+ detail: "A user with this email already exists",
253823
+ error_type: "HTTPException",
253824
+ message: "A user with this email already exists",
253825
+ request_id: null,
253826
+ traceback: ""
253827
+ });
253828
+ return;
253829
+ }
253830
+ const privateUserCollection = db2.getCollection(PRIVATE_USER_COLLECTION);
253831
+ const privateUserData = await privateUserCollection?.findOneAsync({
253832
+ email: email3
253833
+ });
253834
+ const otpCode = generateCode();
253835
+ const id2 = privateUserData ? privateUserData.id : nanoid3();
253836
+ if (!privateUserData) {
253837
+ await privateUserCollection?.insertAsync({
253838
+ id: id2,
253839
+ email: email3,
253840
+ otpCode,
253841
+ password,
253842
+ createdAt: Date.now()
253843
+ });
253844
+ } else {
253845
+ await privateUserCollection?.updateAsync({
253846
+ email: email3
253847
+ }, {
253848
+ $set: {
253849
+ otpCode,
253850
+ createdAt: Date.now()
253851
+ }
253852
+ });
253853
+ }
253854
+ logger2.log(theme.styles.info(`
253855
+ In order to complete registration use this verification code: ${otpCode}
253856
+ `));
253857
+ res.json({
253858
+ id: id2,
253859
+ message: "Registration successful. Please check your email for the verification code.",
253860
+ otp_expires_in_minutes: 10
253861
+ });
253862
+ });
253863
+ router.post("/verify-otp", parseBody, async (req, res) => {
253864
+ const { email: email3, otp_code } = VerifyOtpBody.parse(req.body);
253865
+ const privateUserCollection = db2.getCollection(PRIVATE_USER_COLLECTION);
253866
+ const privateUserData = await privateUserCollection?.findOneAsync({
253867
+ email: email3
253868
+ });
253869
+ if (!privateUserData || privateUserData.otpCode !== otp_code) {
253870
+ const appId = req.params.appId;
253871
+ res.status(500).json({
253872
+ detail: `{'email': '${email3}', 'app_id': '${appId}}'} -> Object not found`,
253873
+ error_type: "ObjectNotFoundError",
253874
+ message: `{'email': '${email3}', 'app_id': '${appId}}'} -> Object not found`,
253875
+ request_id: null,
253876
+ traceback: ""
253877
+ });
253878
+ return;
253879
+ }
253880
+ if (+Date.now() - privateUserData.createdAt > TEN_MINUTES) {
253881
+ res.status(400).json({
253882
+ detail: "Verification code has expired",
253883
+ error_type: "HTTPException",
253884
+ message: "Verification code has expired",
253885
+ request_id: null,
253886
+ traceback: ""
253887
+ });
253888
+ } else {
253889
+ await privateUserCollection?.updateAsync({
253890
+ email: email3
253891
+ }, {
253892
+ $unset: { otpCode: true }
253893
+ });
253894
+ const collection = db2.getCollection(USER_COLLECTION);
253895
+ const now = getNowISOTimestamp();
253896
+ const nameFromEmailMatch = /^([^@]+)/.exec(email3);
253897
+ const fullName = nameFromEmailMatch ? nameFromEmailMatch[1] : email3;
253898
+ await collection?.insertAsync({
253899
+ id: privateUserData.id,
253900
+ email: email3,
253901
+ full_name: fullName,
253902
+ is_service: false,
253903
+ is_verified: true,
253904
+ disabled: null,
253905
+ role: "user",
253906
+ collaborator_role: "editor",
253907
+ created_date: now,
253908
+ updated_date: now
253909
+ });
253910
+ res.json({
253911
+ id: privateUserData.id,
253912
+ access_token: createJwtToken(email3),
253913
+ message: "Email verified successfully. You are now logged in.",
253914
+ success: true
253915
+ });
253916
+ }
253917
+ });
253918
+ return router;
253919
+ }
253920
+
253921
+ // src/cli/dev/dev-server/routes/entities/entities-router.ts
253922
+ var import_express4 = __toESM(require_express(), 1);
253923
+ var import_jsonwebtoken3 = __toESM(require_jsonwebtoken(), 1);
253924
+
253925
+ // src/cli/dev/dev-server/db/rls.ts
253926
+ function resolveTemplate(value, user) {
253927
+ return value.replace(/\{\{user\.([\w.]+)\}\}/g, (_match, path18) => {
253928
+ if (path18.startsWith("data.")) {
253929
+ return String(user[path18.slice(5)] ?? "");
253930
+ }
253931
+ return String(user[path18] ?? "");
253932
+ });
253933
+ }
253934
+ function getRecordField(key2, record2) {
253935
+ if (key2.startsWith("data.")) {
253936
+ return record2[key2.slice(5)];
253937
+ }
253938
+ return record2[key2];
253939
+ }
253940
+ function evaluateOperator(recordValue, operator) {
253941
+ for (const [op2, opValue] of Object.entries(operator)) {
253942
+ switch (op2) {
253943
+ case "$in":
253944
+ if (!Array.isArray(opValue) || !opValue.includes(recordValue)) {
253945
+ return false;
253946
+ }
253947
+ break;
253948
+ case "$nin":
253949
+ if (!Array.isArray(opValue)) {
253950
+ return false;
253951
+ }
253952
+ if (Array.isArray(opValue) && opValue.includes(recordValue)) {
253953
+ return false;
253954
+ }
253955
+ break;
253956
+ case "$ne":
253957
+ if (recordValue === opValue)
253958
+ return false;
253959
+ break;
253960
+ case "$all":
253961
+ if (!Array.isArray(recordValue) || !Array.isArray(opValue)) {
253962
+ return false;
253963
+ }
253964
+ if (!opValue.every((v10) => recordValue.includes(v10))) {
253965
+ return false;
253966
+ }
253967
+ break;
253968
+ }
253969
+ }
253970
+ return true;
253971
+ }
253972
+ function evaluateUserCondition(condition, user) {
253973
+ for (const [key2, expected] of Object.entries(condition)) {
253974
+ const userValue = key2.startsWith("data.") ? user[key2.slice(5)] : user[key2];
253975
+ if (typeof expected === "object" && expected !== null) {
253976
+ if (!evaluateOperator(userValue, expected))
253977
+ return false;
253978
+ } else {
253979
+ if (userValue !== expected)
253980
+ return false;
253981
+ }
253982
+ }
253983
+ return true;
253984
+ }
253985
+ function evaluateCondition(condition, record2, user) {
253986
+ for (const [key2, value] of Object.entries(condition)) {
253987
+ if (key2 === "user_condition") {
253988
+ if (!evaluateUserCondition(value, user))
253989
+ return false;
253990
+ continue;
253991
+ }
253992
+ if (key2 === "$or") {
253993
+ const conditions = value;
253994
+ if (!conditions.some((c8) => evaluateCondition(c8, record2, user)))
253995
+ return false;
253996
+ continue;
253997
+ }
253998
+ if (key2 === "$and") {
253999
+ const conditions = value;
254000
+ if (!conditions.every((c8) => evaluateCondition(c8, record2, user)))
254001
+ return false;
254002
+ continue;
254003
+ }
254004
+ if (key2 === "$nor") {
254005
+ const conditions = value;
254006
+ if (conditions.some((c8) => evaluateCondition(c8, record2, user)))
254007
+ return false;
254008
+ continue;
254009
+ }
254010
+ const recordValue = getRecordField(key2, record2);
254011
+ const resolvedValue = typeof value === "string" ? resolveTemplate(value, user) : value;
254012
+ if (typeof resolvedValue === "object" && resolvedValue !== null) {
254013
+ if (!evaluateOperator(recordValue, resolvedValue))
254014
+ return false;
254015
+ } else {
254016
+ if (recordValue !== resolvedValue)
254017
+ return false;
254018
+ }
254019
+ }
254020
+ return true;
254021
+ }
254022
+ function checkRLS(rule, record2, user) {
254023
+ if (rule === undefined)
254024
+ return true;
254025
+ if (typeof rule === "boolean")
254026
+ return rule;
254027
+ if (!user)
254028
+ return false;
254029
+ return evaluateCondition(rule, record2, user);
254030
+ }
254031
+ function applyFLS(record2, schema10, user, operation) {
254032
+ const result = {};
254033
+ for (const [key2, value] of Object.entries(record2)) {
254034
+ const rule = schema10.properties[key2]?.rls?.[operation];
254035
+ if (!rule || checkRLS(rule, record2, user)) {
254036
+ result[key2] = value;
254037
+ }
254038
+ }
254039
+ return result;
254040
+ }
254041
+
254042
+ // src/cli/dev/dev-server/db/entity-queries.ts
254043
+ function parseSort(sort) {
254044
+ if (!sort) {
254045
+ return;
254046
+ }
254047
+ if (sort.startsWith("-")) {
254048
+ return { [sort.slice(1)]: -1 };
254049
+ }
254050
+ return { [sort]: 1 };
254051
+ }
254052
+ function parseFields(fields) {
254053
+ if (!fields) {
254054
+ return;
254055
+ }
254056
+ const projection = {};
254057
+ for (const field of fields.split(",")) {
254058
+ const trimmed = field.trim();
254059
+ if (trimmed) {
254060
+ projection[trimmed] = 1;
254061
+ }
254062
+ }
254063
+ return Object.keys(projection).length > 0 ? projection : undefined;
254064
+ }
254065
+ var queryEntity = async (collection, reqQuery) => {
254066
+ const { sort, limit, skip: skip2, fields, q: q13 } = reqQuery;
254067
+ let query = {};
254068
+ if (q13 && typeof q13 === "string") {
254069
+ try {
254070
+ query = JSON.parse(q13);
254071
+ } catch {
254072
+ throw new InvalidInputError("Invalid query parameter 'q'");
254073
+ }
254074
+ }
254075
+ let cursor3 = collection.findAsync(query);
254076
+ const sortObj = parseSort(sort);
254077
+ if (sortObj) {
254078
+ cursor3 = cursor3.sort(sortObj);
254079
+ }
254080
+ if (skip2) {
254081
+ const skipNum = Number.parseInt(skip2, 10);
254082
+ if (!Number.isNaN(skipNum)) {
254083
+ cursor3 = cursor3.skip(skipNum);
254084
+ }
254085
+ }
254086
+ if (limit) {
254087
+ const limitNum = Number.parseInt(limit, 10);
254088
+ if (!Number.isNaN(limitNum)) {
254089
+ cursor3 = cursor3.limit(limitNum);
254090
+ }
254091
+ }
254092
+ const projection = parseFields(fields);
254093
+ if (projection) {
254094
+ cursor3 = cursor3.projection(projection);
254095
+ }
254096
+ return cursor3;
254097
+ };
254098
+
254099
+ // src/cli/dev/dev-server/routes/entities/entities-user-router.ts
254100
+ var import_express3 = __toESM(require_express(), 1);
254101
+ var import_jsonwebtoken2 = __toESM(require_jsonwebtoken(), 1);
254102
+ function createUserRouter(db2, logger2) {
254103
+ const router = import_express3.Router({ mergeParams: true });
254104
+ const parseBody = import_express3.json();
253775
254105
  function withAuth(handler) {
253776
254106
  return async (req, res) => {
253777
254107
  const auth2 = req.headers.authorization;
@@ -253780,13 +254110,13 @@ function createUserRouter(db2, logger2) {
253780
254110
  return;
253781
254111
  }
253782
254112
  try {
253783
- const { payload } = import_jsonwebtoken.default.decode(auth2.replace("Bearer ", ""), { complete: true }) ?? {};
253784
- const result = await db2.getCollection(USER_COLLECTION)?.findOneAsync({ email: payload?.sub });
253785
- if (!result) {
254113
+ const { payload } = import_jsonwebtoken2.default.decode(auth2.replace("Bearer ", ""), { complete: true }) ?? {};
254114
+ const currentUser = await db2.getCollection(USER_COLLECTION)?.findOneAsync({ email: payload?.sub });
254115
+ if (!currentUser) {
253786
254116
  res.status(404).json({ error: "Unable to read data for the current user" });
253787
254117
  return;
253788
254118
  }
253789
- await handler(req, res, result);
254119
+ await handler(req, res, currentUser);
253790
254120
  } catch {
253791
254121
  res.status(401).json({ error: "Unauthorized" });
253792
254122
  }
@@ -253813,6 +254143,28 @@ function createUserRouter(db2, logger2) {
253813
254143
  ...req.body
253814
254144
  });
253815
254145
  }));
254146
+ router.get("/", withAuth(async (req, res, currentUser) => {
254147
+ const collection = db2.getCollection(USER_COLLECTION);
254148
+ if (!collection) {
254149
+ res.status(404).json({ error: `Entity "${USER_COLLECTION}" not found` });
254150
+ return;
254151
+ }
254152
+ try {
254153
+ if (currentUser.role === "admin") {
254154
+ const result = await queryEntity(collection, req.query);
254155
+ res.json(stripInternalFields(result));
254156
+ } else {
254157
+ res.json([stripInternalFields(currentUser)]);
254158
+ }
254159
+ } catch (error48) {
254160
+ if (error48 instanceof InvalidInputError) {
254161
+ res.status(400).json({ error: error48.message });
254162
+ } else {
254163
+ logger2.error(`Error in GET /${USER_COLLECTION}:`, error48);
254164
+ res.status(500).json({ error: "Internal server error" });
254165
+ }
254166
+ }
254167
+ }));
253816
254168
  router.post("/bulk", async (_req, res) => {
253817
254169
  res.json({});
253818
254170
  });
@@ -253862,31 +254214,9 @@ function createUserRouter(db2, logger2) {
253862
254214
  }
253863
254215
 
253864
254216
  // src/cli/dev/dev-server/routes/entities/entities-router.ts
253865
- function parseSort(sort) {
253866
- if (!sort) {
253867
- return;
253868
- }
253869
- if (sort.startsWith("-")) {
253870
- return { [sort.slice(1)]: -1 };
253871
- }
253872
- return { [sort]: 1 };
253873
- }
253874
- function parseFields(fields) {
253875
- if (!fields) {
253876
- return;
253877
- }
253878
- const projection = {};
253879
- for (const field of fields.split(",")) {
253880
- const trimmed = field.trim();
253881
- if (trimmed) {
253882
- projection[trimmed] = 1;
253883
- }
253884
- }
253885
- return Object.keys(projection).length > 0 ? projection : undefined;
253886
- }
253887
254217
  async function createEntityRoutes(db2, logger2, broadcast) {
253888
- const router = import_express3.Router({ mergeParams: true });
253889
- const parseBody = import_express3.json();
254218
+ const router = import_express4.Router({ mergeParams: true });
254219
+ const parseBody = import_express4.json();
253890
254220
  function withCollection(handler) {
253891
254221
  return async (req, res) => {
253892
254222
  const collection = db2.getCollection(req.params.entityName);
@@ -253894,7 +254224,13 @@ async function createEntityRoutes(db2, logger2, broadcast) {
253894
254224
  res.status(404).json({ error: `Entity "${req.params.entityName}" not found` });
253895
254225
  return;
253896
254226
  }
253897
- await handler(req, res, collection);
254227
+ let currentUser;
254228
+ try {
254229
+ const auth2 = req.headers.authorization || "";
254230
+ const { payload } = import_jsonwebtoken3.default.decode(auth2.replace("Bearer ", ""), { complete: true }) ?? {};
254231
+ currentUser = await db2.getCollection(USER_COLLECTION)?.findOneAsync({ email: payload?.sub });
254232
+ } catch {}
254233
+ await handler(req, res, collection, currentUser);
253898
254234
  };
253899
254235
  }
253900
254236
  function emit(appId, entityName, type, data) {
@@ -253914,7 +254250,7 @@ async function createEntityRoutes(db2, logger2, broadcast) {
253914
254250
  }
253915
254251
  const userRouter = createUserRouter(db2, logger2);
253916
254252
  router.use("/User", userRouter);
253917
- router.get("/:entityName/:id", withCollection(async (req, res, collection) => {
254253
+ router.get("/:entityName/:id", withCollection(async (req, res, collection, currentUser) => {
253918
254254
  const { entityName, id: id2 } = req.params;
253919
254255
  try {
253920
254256
  const doc2 = await collection.findOneAsync({ id: id2 });
@@ -253922,63 +254258,72 @@ async function createEntityRoutes(db2, logger2, broadcast) {
253922
254258
  res.status(404).json({ error: `Record with id "${id2}" not found` });
253923
254259
  return;
253924
254260
  }
253925
- res.json(stripInternalFields(doc2));
254261
+ const schema10 = db2.getSchema(entityName);
254262
+ if (!checkRLS(schema10?.rls?.read, doc2, currentUser)) {
254263
+ res.status(404).json({
254264
+ message: `Entity ${entityName} with ID ${id2} not found`
254265
+ });
254266
+ return;
254267
+ }
254268
+ let result = stripInternalFields(doc2);
254269
+ if (schema10) {
254270
+ result = applyFLS(result, schema10, currentUser, "read");
254271
+ }
254272
+ res.json(result);
253926
254273
  } catch (error48) {
253927
254274
  logger2.error(`Error in GET /${entityName}/${id2}:`, error48);
253928
254275
  res.status(500).json({ error: "Internal server error" });
253929
254276
  }
253930
254277
  }));
253931
- router.get("/:entityName", withCollection(async (req, res, collection) => {
254278
+ router.get("/:entityName", withCollection(async (req, res, collection, currentUser) => {
253932
254279
  const { entityName } = req.params;
253933
254280
  try {
253934
- const { sort, limit, skip: skip2, fields, q: q13 } = req.query;
253935
- let query = {};
253936
- if (q13 && typeof q13 === "string") {
253937
- try {
253938
- query = JSON.parse(q13);
253939
- } catch {
253940
- res.status(400).json({ error: "Invalid query parameter 'q'" });
253941
- return;
253942
- }
253943
- }
253944
- let cursor3 = collection.findAsync(query);
253945
- const sortObj = parseSort(sort);
253946
- if (sortObj) {
253947
- cursor3 = cursor3.sort(sortObj);
253948
- }
253949
- if (skip2) {
253950
- const skipNum = Number.parseInt(skip2, 10);
253951
- if (!Number.isNaN(skipNum)) {
253952
- cursor3 = cursor3.skip(skipNum);
253953
- }
254281
+ const schema10 = db2.getSchema(entityName);
254282
+ if (schema10?.rls?.read === false) {
254283
+ res.json([]);
254284
+ return;
253954
254285
  }
253955
- if (limit) {
253956
- const limitNum = Number.parseInt(limit, 10);
253957
- if (!Number.isNaN(limitNum)) {
253958
- cursor3 = cursor3.limit(limitNum);
253959
- }
254286
+ let results = stripInternalFields(await queryEntity(collection, req.query));
254287
+ if (schema10?.rls?.read && schema10.rls.read !== true) {
254288
+ results = results.filter((doc2) => checkRLS(schema10.rls.read, doc2, currentUser));
253960
254289
  }
253961
- const projection = parseFields(fields);
253962
- if (projection) {
253963
- cursor3 = cursor3.projection(projection);
254290
+ if (schema10) {
254291
+ results = results.map((doc2) => applyFLS(doc2, schema10, currentUser, "read"));
253964
254292
  }
253965
- const docs = await cursor3;
253966
- res.json(stripInternalFields(docs));
254293
+ res.json(results);
253967
254294
  } catch (error48) {
253968
- logger2.error(`Error in GET /${entityName}:`, error48);
253969
- res.status(500).json({ error: "Internal server error" });
254295
+ if (error48 instanceof InvalidInputError) {
254296
+ res.status(400).json({ error: error48.message });
254297
+ } else {
254298
+ logger2.error(`Error in GET /${entityName}:`, error48);
254299
+ res.status(500).json({ error: "Internal server error" });
254300
+ }
253970
254301
  }
253971
254302
  }));
253972
- router.post("/:entityName", parseBody, withCollection(async (req, res, collection) => {
254303
+ router.post("/:entityName", parseBody, withCollection(async (req, res, collection, currentUser) => {
253973
254304
  const { appId, entityName } = req.params;
253974
254305
  try {
253975
254306
  const now = new Date().toISOString();
253976
254307
  const { _id, ...body } = req.body;
253977
- const filteredBody = db2.prepareRecord(entityName, body);
254308
+ const schema10 = db2.getSchema(entityName);
254309
+ if (!checkRLS(schema10?.rls?.create, {
254310
+ ...body,
254311
+ created_by: currentUser?.email,
254312
+ created_by_id: currentUser?.id
254313
+ }, currentUser)) {
254314
+ res.status(403).json({ error: "Permission denied" });
254315
+ return;
254316
+ }
254317
+ let filteredBody = db2.prepareRecord(entityName, body);
254318
+ if (schema10) {
254319
+ filteredBody = applyFLS(filteredBody, schema10, currentUser, "write");
254320
+ }
253978
254321
  db2.validate(entityName, filteredBody);
253979
254322
  const record2 = {
253980
254323
  ...filteredBody,
253981
254324
  id: nanoid3(),
254325
+ created_by: currentUser?.email,
254326
+ created_by_id: currentUser?.id,
253982
254327
  created_date: now,
253983
254328
  updated_date: now
253984
254329
  };
@@ -253994,7 +254339,7 @@ async function createEntityRoutes(db2, logger2, broadcast) {
253994
254339
  res.status(500).json({ error: "Internal server error" });
253995
254340
  }
253996
254341
  }));
253997
- router.post("/:entityName/bulk", parseBody, withCollection(async (req, res, collection) => {
254342
+ router.post("/:entityName/bulk", parseBody, withCollection(async (req, res, collection, currentUser) => {
253998
254343
  const { appId, entityName } = req.params;
253999
254344
  if (!Array.isArray(req.body)) {
254000
254345
  res.status(400).json({ error: "Request body must be an array" });
@@ -254002,13 +254347,27 @@ async function createEntityRoutes(db2, logger2, broadcast) {
254002
254347
  }
254003
254348
  try {
254004
254349
  const now = new Date().toISOString();
254350
+ const schema10 = db2.getSchema(entityName);
254005
254351
  const records = [];
254006
254352
  for (const record2 of req.body) {
254007
- const filteredRecord = db2.prepareRecord(entityName, record2);
254353
+ if (!checkRLS(schema10?.rls?.create, {
254354
+ ...record2,
254355
+ created_by: currentUser?.email,
254356
+ created_by_id: currentUser?.id
254357
+ }, currentUser)) {
254358
+ res.status(403).json({ error: "Permission denied" });
254359
+ return;
254360
+ }
254361
+ let filteredRecord = db2.prepareRecord(entityName, record2);
254362
+ if (schema10) {
254363
+ filteredRecord = applyFLS(filteredRecord, schema10, currentUser, "write");
254364
+ }
254008
254365
  db2.validate(entityName, filteredRecord);
254009
254366
  records.push({
254010
254367
  ...filteredRecord,
254011
254368
  id: nanoid3(),
254369
+ created_by: currentUser?.email,
254370
+ created_by_id: currentUser?.id,
254012
254371
  created_date: now,
254013
254372
  updated_date: now
254014
254373
  });
@@ -254025,11 +254384,28 @@ async function createEntityRoutes(db2, logger2, broadcast) {
254025
254384
  res.status(500).json({ error: "Internal server error" });
254026
254385
  }
254027
254386
  }));
254028
- router.put("/:entityName/:id", parseBody, withCollection(async (req, res, collection) => {
254387
+ router.put("/:entityName/:id", parseBody, withCollection(async (req, res, collection, currentUser) => {
254029
254388
  const { appId, entityName, id: id2 } = req.params;
254030
254389
  const { id: _id, created_date: _created_date, ...body } = req.body;
254031
254390
  try {
254032
- const filteredBody = db2.prepareRecord(entityName, body, true);
254391
+ const schema10 = db2.getSchema(entityName);
254392
+ if (schema10?.rls?.update !== undefined) {
254393
+ const existing = await collection.findOneAsync({ id: id2 });
254394
+ if (!existing) {
254395
+ res.status(404).json({ error: `Record with id "${id2}" not found` });
254396
+ return;
254397
+ }
254398
+ if (!checkRLS(schema10.rls.update, existing, currentUser)) {
254399
+ res.status(404).json({
254400
+ message: `Entity ${entityName} with ID ${id2} not found`
254401
+ });
254402
+ return;
254403
+ }
254404
+ }
254405
+ let filteredBody = db2.prepareRecord(entityName, body, true);
254406
+ if (schema10) {
254407
+ filteredBody = applyFLS(filteredBody, schema10, currentUser, "write");
254408
+ }
254033
254409
  db2.validate(entityName, filteredBody, true);
254034
254410
  const updateData = {
254035
254411
  ...filteredBody,
@@ -254052,30 +254428,50 @@ async function createEntityRoutes(db2, logger2, broadcast) {
254052
254428
  res.status(500).json({ error: "Internal server error" });
254053
254429
  }
254054
254430
  }));
254055
- router.delete("/:entityName/:id", withCollection(async (req, res, collection) => {
254431
+ router.delete("/:entityName/:id", withCollection(async (req, res, collection, currentUser) => {
254056
254432
  const { appId, entityName, id: id2 } = req.params;
254057
254433
  try {
254058
254434
  const doc2 = await collection.findOneAsync({ id: id2 });
254059
- const numRemoved = await collection.removeAsync({ id: id2 }, { multi: false });
254060
- if (numRemoved === 0) {
254435
+ if (!doc2) {
254061
254436
  res.status(404).json({ error: `Record with id "${id2}" not found` });
254062
254437
  return;
254063
254438
  }
254064
- if (doc2) {
254065
- emit(appId, entityName, "delete", stripInternalFields(doc2));
254439
+ const schema10 = db2.getSchema(entityName);
254440
+ if (!checkRLS(schema10?.rls?.delete, doc2, currentUser)) {
254441
+ res.status(404).json({
254442
+ message: `Entity ${entityName} with ID ${id2} not found`
254443
+ });
254444
+ return;
254066
254445
  }
254446
+ await collection.removeAsync({ id: id2 }, { multi: false });
254447
+ emit(appId, entityName, "delete", stripInternalFields(doc2));
254067
254448
  res.json({ success: true });
254068
254449
  } catch (error48) {
254069
254450
  logger2.error(`Error in DELETE /${entityName}/${id2}:`, error48);
254070
254451
  res.status(500).json({ error: "Internal server error" });
254071
254452
  }
254072
254453
  }));
254073
- router.delete("/:entityName", parseBody, withCollection(async (req, res, collection) => {
254454
+ router.delete("/:entityName", parseBody, withCollection(async (req, res, collection, currentUser) => {
254074
254455
  const { entityName } = req.params;
254075
254456
  try {
254076
254457
  const query = req.body || {};
254077
- const numRemoved = await collection.removeAsync(query, { multi: true });
254078
- res.json({ success: true, deleted: numRemoved });
254458
+ const schema10 = db2.getSchema(entityName);
254459
+ const rlsDelete = schema10?.rls?.delete;
254460
+ if (rlsDelete !== undefined && rlsDelete !== true) {
254461
+ if (rlsDelete === false) {
254462
+ res.status(403).json({ error: "Permission denied" });
254463
+ return;
254464
+ }
254465
+ const docs = await collection.findAsync(query);
254466
+ const allowedIds = docs.filter((doc2) => checkRLS(rlsDelete, doc2, currentUser)).map((doc2) => doc2.id);
254467
+ const numRemoved = await collection.removeAsync({ id: { $in: allowedIds } }, { multi: true });
254468
+ res.json({ success: true, deleted: numRemoved });
254469
+ } else {
254470
+ const numRemoved = await collection.removeAsync(query, {
254471
+ multi: true
254472
+ });
254473
+ res.json({ success: true, deleted: numRemoved });
254474
+ }
254079
254475
  } catch (error48) {
254080
254476
  logger2.error(`Error in DELETE /${entityName}:`, error48);
254081
254477
  res.status(500).json({ error: "Internal server error" });
@@ -254085,7 +254481,7 @@ async function createEntityRoutes(db2, logger2, broadcast) {
254085
254481
  }
254086
254482
 
254087
254483
  // src/cli/dev/dev-server/routes/integrations.ts
254088
- var import_express4 = __toESM(require_express(), 1);
254484
+ var import_express5 = __toESM(require_express(), 1);
254089
254485
  var import_multer = __toESM(require_multer(), 1);
254090
254486
  import { createHash, randomUUID as randomUUID4 } from "node:crypto";
254091
254487
  import fs28 from "node:fs";
@@ -254094,8 +254490,8 @@ function createFileToken(fileUri) {
254094
254490
  return createHash("sha256").update(fileUri).digest("hex");
254095
254491
  }
254096
254492
  function createIntegrationRoutes(mediaFilesDir, baseUrl, remoteProxy, logger2) {
254097
- const router = import_express4.Router({ mergeParams: true });
254098
- const parseBody = import_express4.json();
254493
+ const router = import_express5.Router({ mergeParams: true });
254494
+ const parseBody = import_express5.json();
254099
254495
  const privateFilesDir = path18.join(mediaFilesDir, "private");
254100
254496
  fs28.mkdirSync(mediaFilesDir, { recursive: true });
254101
254497
  fs28.mkdirSync(privateFilesDir, { recursive: true });
@@ -254165,7 +254561,7 @@ function createIntegrationRoutes(mediaFilesDir, baseUrl, remoteProxy, logger2) {
254165
254561
  return router;
254166
254562
  }
254167
254563
  function createCustomIntegrationRoutes(remoteProxy, logger2) {
254168
- const router = import_express4.Router({ mergeParams: true });
254564
+ const router = import_express5.Router({ mergeParams: true });
254169
254565
  router.post("/:slug/:operationId", (req, res, next) => {
254170
254566
  logger2.warn(`"${req.originalUrl}" is not supported in local development, passing call to production`);
254171
254567
  req.url = req.originalUrl;
@@ -255878,7 +256274,7 @@ async function createDevServer(options8) {
255878
256274
  const port = userPort ?? await getPorts({ port: DEFAULT_PORT });
255879
256275
  const baseUrl = `http://localhost:${port}`;
255880
256276
  const { functions, entities, project: project2 } = await options8.loadResources();
255881
- const app = import_express5.default();
256277
+ const app = import_express6.default();
255882
256278
  const remoteProxy = import_http_proxy_middleware2.createProxyMiddleware({
255883
256279
  target: BASE44_APP_URL,
255884
256280
  changeOrigin: true
@@ -255910,6 +256306,8 @@ async function createDevServer(options8) {
255910
256306
  let emitEntityEvent = () => {};
255911
256307
  const entityRoutes = await createEntityRoutes(db2, devLogger, (...args) => emitEntityEvent(...args));
255912
256308
  app.use("/api/apps/:appId/entities", entityRoutes);
256309
+ const authRouter = createAuthRouter(db2, devLogger);
256310
+ app.use("/api/apps/:appId/auth", authRouter);
255913
256311
  const { path: mediaFilesDir } = await $dir();
255914
256312
  app.use("/media/private/:fileUri", (req, res, next) => {
255915
256313
  const { fileUri } = req.params;
@@ -255929,13 +256327,15 @@ async function createDevServer(options8) {
255929
256327
  }
255930
256328
  next();
255931
256329
  });
255932
- app.use("/media", import_express5.default.static(mediaFilesDir));
256330
+ app.use("/media", import_express6.default.static(mediaFilesDir));
255933
256331
  const integrationRoutes = createIntegrationRoutes(mediaFilesDir, baseUrl, remoteProxy, devLogger);
255934
256332
  app.use("/api/apps/:appId/integration-endpoints", integrationRoutes);
255935
256333
  const customIntegrationRoutes = createCustomIntegrationRoutes(remoteProxy, devLogger);
255936
256334
  app.use("/api/apps/:appId/integrations/custom", customIntegrationRoutes);
255937
256335
  app.use((req, res, next) => {
255938
- devLogger.warn(`"${req.originalUrl}" is not supported in local development, passing call to production`);
256336
+ if (!req.originalUrl.endsWith("analytics/track/batch")) {
256337
+ devLogger.warn(`"${req.originalUrl}" is not supported in local development, passing call to production`);
256338
+ }
255939
256339
  remoteProxy(req, res, next);
255940
256340
  });
255941
256341
  const server = await new Promise((resolve9, reject) => {
@@ -256006,6 +256406,7 @@ async function devAction({ log }, options8) {
256006
256406
  const { port: resolvedPort } = await createDevServer({
256007
256407
  log,
256008
256408
  port,
256409
+ cwd: process21.cwd(),
256009
256410
  denoWrapperPath: getDenoWrapperPath(),
256010
256411
  loadResources: async () => {
256011
256412
  const { functions, entities, project: project2 } = await readProjectConfig();
@@ -260482,4 +260883,4 @@ export {
260482
260883
  CLIExitError
260483
260884
  };
260484
260885
 
260485
- //# debugId=57ABEAB0FFF040D064756E2164756E21
260886
+ //# debugId=759CD39E93FDA9AF64756E2164756E21