@atlashub/smartstack-cli 1.19.0 → 1.21.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/dist/index.js CHANGED
@@ -115807,11 +115807,7 @@ EndGlobal
115807
115807
  );
115808
115808
  }
115809
115809
  logger.info("Installing NuGet packages...");
115810
- const dbPackage = {
115811
- sqlserver: "Microsoft.EntityFrameworkCore.SqlServer",
115812
- postgresql: "Npgsql.EntityFrameworkCore.PostgreSQL",
115813
- sqlite: "Microsoft.EntityFrameworkCore.Sqlite"
115814
- }[config.database];
115810
+ const dbPackage = "Microsoft.EntityFrameworkCore.SqlServer";
115815
115811
  const nugetPackages = [
115816
115812
  {
115817
115813
  project: `${projectName}.Domain`,
@@ -115924,11 +115920,7 @@ async function createConfigFiles(config, dryRun) {
115924
115920
  </Project>
115925
115921
  `;
115926
115922
  await import_fs_extra3.default.writeFile((0, import_path4.join)(projectDir, "Directory.Build.props"), buildProps);
115927
- const connectionString = {
115928
- sqlserver: `Server=(local);Database=${projectName};Integrated Security=true;TrustServerCertificate=true;Connection Timeout=60;Pooling=true;Min Pool Size=0;Max Pool Size=50;Load Balance Timeout=30`,
115929
- postgresql: `Host=localhost;Database=${projectName.toLowerCase()};Username=postgres;Password=postgres;Pooling=true;Minimum Pool Size=0;Maximum Pool Size=50`,
115930
- sqlite: `Data Source=${projectName.toLowerCase()}.db`
115931
- }[config.database];
115923
+ const connectionString = `Server=(local);Database=${projectName};Integrated Security=true;TrustServerCertificate=true;Connection Timeout=60;Pooling=true;Min Pool Size=0;Max Pool Size=50;Load Balance Timeout=30`;
115932
115924
  const appSettings = {
115933
115925
  ConnectionStrings: {
115934
115926
  DefaultConnection: connectionString
@@ -116068,6 +116060,13 @@ async function createConfigFiles(config, dryRun) {
116068
116060
  DefaultConflictResolution: "ManualReview",
116069
116061
  AutoCreateReferences: true
116070
116062
  },
116063
+ MultiTenant: {
116064
+ Enabled: config.multiTenant.enabled,
116065
+ EnableB2C: config.multiTenant.enableB2C,
116066
+ SystemTenantSlug: config.multiTenant.systemTenantSlug,
116067
+ SystemTenantName: config.multiTenant.systemTenantName,
116068
+ AutoAssignUsersToSystemTenant: config.multiTenant.autoAssignUsersToSystemTenant
116069
+ },
116071
116070
  AllowedHosts: "*"
116072
116071
  };
116073
116072
  await import_fs_extra3.default.writeFile(
@@ -116641,7 +116640,7 @@ async function initializeGit(config, dryRun) {
116641
116640
  execCommand("git add .", projectDir, dryRun);
116642
116641
  execCommand('git commit -m "chore: initial SmartStack project setup"', projectDir, dryRun);
116643
116642
  }
116644
- var initCommand = new Command("init").description("Initialize a new SmartStack project").argument("<name>", "Project name").option("--db <type>", "Database type (sqlserver, postgresql, sqlite)", "sqlserver").option("--dry-run", "Show what would be created without actually creating").option("-y, --yes", "Skip prompts and use defaults").option("--skip-mcp-check", "Skip MCP servers verification").action(async (name, options) => {
116643
+ var initCommand = new Command("init").description("Initialize a new SmartStack project").argument("<name>", "Project name").option("--dry-run", "Show what would be created without actually creating").option("-y, --yes", "Skip prompts and use defaults").option("--skip-mcp-check", "Skip MCP servers verification").option("--multi-tenant", "Enable multi-tenant mode").option("--b2c", "Enable B2C (user tenant management)").action(async (name, options) => {
116645
116644
  logger.header("SmartStack Project Initialization");
116646
116645
  if (!options.skipMcpCheck) {
116647
116646
  logger.info("Checking MCP servers...");
@@ -116719,39 +116718,56 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
116719
116718
  config = {
116720
116719
  name,
116721
116720
  nameLower: name.toLowerCase(),
116722
- database: options.db || "sqlserver",
116723
- modules: ["Auth", "Navigation", "Notifications"]
116721
+ database: "sqlserver",
116722
+ multiTenant: {
116723
+ enabled: options.multiTenant ?? true,
116724
+ enableB2C: options.b2c ?? true,
116725
+ systemTenantSlug: "default",
116726
+ systemTenantName: "Default Workspace",
116727
+ autoAssignUsersToSystemTenant: true
116728
+ }
116724
116729
  };
116725
116730
  } else {
116726
116731
  const answers = await lib_default.prompt([
116727
116732
  {
116728
- type: "list",
116729
- name: "database",
116730
- message: "Which database do you want to use?",
116731
- choices: [
116732
- { name: "SQL Server", value: "sqlserver" },
116733
- { name: "PostgreSQL", value: "postgresql" },
116734
- { name: "SQLite", value: "sqlite" }
116735
- ],
116736
- default: options.db || "sqlserver"
116733
+ type: "confirm",
116734
+ name: "multiTenantEnabled",
116735
+ message: "Enable multi-tenant mode?",
116736
+ default: true
116737
116737
  },
116738
116738
  {
116739
- type: "checkbox",
116740
- name: "modules",
116741
- message: "Which SmartStack modules do you want to enable?",
116742
- choices: [
116743
- { name: "Auth (JWT + OAuth)", value: "Auth", checked: true },
116744
- { name: "Navigation (Dynamic menus)", value: "Navigation", checked: true },
116745
- { name: "AI (OpenAI/Claude integration)", value: "AI", checked: false },
116746
- { name: "Notifications (SignalR real-time)", value: "Notifications", checked: true }
116747
- ]
116739
+ type: "confirm",
116740
+ name: "enableB2C",
116741
+ message: "Enable B2C (user tenant management)?",
116742
+ default: true,
116743
+ when: (answers2) => answers2.multiTenantEnabled
116744
+ },
116745
+ {
116746
+ type: "input",
116747
+ name: "systemTenantSlug",
116748
+ message: "System tenant slug:",
116749
+ default: "default",
116750
+ when: (answers2) => answers2.multiTenantEnabled
116751
+ },
116752
+ {
116753
+ type: "input",
116754
+ name: "systemTenantName",
116755
+ message: "System tenant name:",
116756
+ default: "Default Workspace",
116757
+ when: (answers2) => answers2.multiTenantEnabled
116748
116758
  }
116749
116759
  ]);
116750
116760
  config = {
116751
116761
  name,
116752
116762
  nameLower: name.toLowerCase(),
116753
- database: answers.database || "sqlserver",
116754
- modules: answers.modules || []
116763
+ database: "sqlserver",
116764
+ multiTenant: {
116765
+ enabled: answers.multiTenantEnabled ?? true,
116766
+ enableB2C: answers.enableB2C ?? true,
116767
+ systemTenantSlug: answers.systemTenantSlug || "default",
116768
+ systemTenantName: answers.systemTenantName || "Default Workspace",
116769
+ autoAssignUsersToSystemTenant: true
116770
+ }
116755
116771
  };
116756
116772
  }
116757
116773
  const dryRun = options.dryRun || false;
@@ -116760,8 +116776,12 @@ var initCommand = new Command("init").description("Initialize a new SmartStack p
116760
116776
  }
116761
116777
  console.log();
116762
116778
  logger.info(`Project: ${source_default.cyan(config.name)}`);
116763
- logger.info(`Database: ${source_default.cyan(config.database)}`);
116764
- logger.info(`Modules: ${source_default.cyan(config.modules.join(", ") || "None")}`);
116779
+ logger.info(`Database: ${source_default.cyan("SQL Server")}`);
116780
+ logger.info(`Multi-Tenant: ${config.multiTenant.enabled ? source_default.green("Enabled") : source_default.gray("Disabled")}`);
116781
+ if (config.multiTenant.enabled) {
116782
+ logger.info(` B2C (User Tenants): ${config.multiTenant.enableB2C ? source_default.green("Enabled") : source_default.gray("Disabled")}`);
116783
+ logger.info(` System Tenant: ${source_default.cyan(config.multiTenant.systemTenantSlug)} (${config.multiTenant.systemTenantName})`);
116784
+ }
116765
116785
  console.log();
116766
116786
  try {
116767
116787
  if (!dryRun) {
@@ -117769,6 +117789,7 @@ var import_mssql = __toESM(require_mssql());
117769
117789
  var import_bcryptjs = __toESM(require_bcryptjs());
117770
117790
  var import_fs3 = require("fs");
117771
117791
  var import_path7 = require("path");
117792
+ var import_child_process7 = require("child_process");
117772
117793
  var DEFAULT_CONFIG = {
117773
117794
  schema: "core",
117774
117795
  usersTable: "auth_Users",
@@ -117798,6 +117819,26 @@ function extractConnectionString(filePath) {
117798
117819
  return null;
117799
117820
  }
117800
117821
  }
117822
+ function parseConnectionString(connStr) {
117823
+ const parts = connStr.split(";").reduce(
117824
+ (acc, part) => {
117825
+ const [key, value] = part.split("=").map((s) => s.trim());
117826
+ if (key && value) acc[key.toLowerCase()] = value;
117827
+ return acc;
117828
+ },
117829
+ {}
117830
+ );
117831
+ return {
117832
+ server: parts["server"] || parts["data source"] || "localhost",
117833
+ database: parts["database"] || parts["initial catalog"] || "SmartStack",
117834
+ useWindowsAuth: parts["integrated security"]?.toLowerCase() === "true" || parts["trusted_connection"]?.toLowerCase() === "true"
117835
+ };
117836
+ }
117837
+ function executeSqlCmd(server, database, query) {
117838
+ const sqlServer = server === "(local)" ? "." : server;
117839
+ const cmd = `sqlcmd -S "${sqlServer}" -d "${database}" -E -I -Q "${query.replace(/"/g, '\\"')}" -h -1 -W`;
117840
+ return (0, import_child_process7.execSync)(cmd, { encoding: "utf-8" }).trim();
117841
+ }
117801
117842
  function detectSmartStackApp() {
117802
117843
  const possiblePaths = [
117803
117844
  "D:\\01 - projets\\SmartStack.app\\02-Develop\\src\\SmartStack.Api",
@@ -117871,56 +117912,85 @@ adminCommand.command("reset").description("Reset the localAdmin account password
117871
117912
  }
117872
117913
  }
117873
117914
  const spinner = logger.spinner("Connecting to database...");
117915
+ const connInfo = parseConnectionString(connectionString);
117874
117916
  try {
117875
- await import_mssql.default.connect(connectionString);
117876
- spinner.text = "Connected. Checking account...";
117877
- const checkQuery = `
117878
- SELECT COUNT(*) as count
117879
- FROM [${DEFAULT_CONFIG.schema}].[${DEFAULT_CONFIG.usersTable}]
117880
- WHERE Email = @email
117881
- `;
117882
- const checkResult = await import_mssql.default.query`
117883
- SELECT COUNT(*) as count
117884
- FROM [core].[auth_Users]
117885
- WHERE Email = ${adminEmail}
117886
- `;
117887
- const exists = checkResult.recordset[0].count > 0;
117888
- if (!exists) {
117889
- spinner.fail(`Account ${source_default.yellow(adminEmail)} does not exist.`);
117890
- logger.error("Cannot reset password for non-existent account.");
117891
- logger.info("Use the application seeding or AdminTool to create the account first.");
117917
+ if (connInfo.useWindowsAuth) {
117918
+ spinner.text = "Using Windows Authentication (sqlcmd)...";
117919
+ const checkQuery = `SELECT COUNT(*) FROM [core].[auth_Users] WHERE Email = '${adminEmail}'`;
117920
+ const countResult = executeSqlCmd(connInfo.server, connInfo.database, checkQuery);
117921
+ const exists = parseInt(countResult, 10) > 0;
117922
+ if (!exists) {
117923
+ spinner.fail(`Account ${source_default.yellow(adminEmail)} does not exist.`);
117924
+ logger.error("Cannot reset password for non-existent account.");
117925
+ logger.info("Use the application seeding or AdminTool to create the account first.");
117926
+ process.exit(1);
117927
+ }
117928
+ spinner.text = "Generating new password...";
117929
+ const newPassword = generatePassword();
117930
+ const passwordHash = await import_bcryptjs.default.hash(newPassword, 10);
117931
+ spinner.text = "Updating password...";
117932
+ const updateQuery = `UPDATE [core].[auth_Users] SET PasswordHash = '${passwordHash}', UpdatedAt = GETUTCDATE() WHERE Email = '${adminEmail}'`;
117933
+ executeSqlCmd(connInfo.server, connInfo.database, updateQuery);
117934
+ spinner.succeed("Password reset successfully!");
117935
+ console.log();
117936
+ console.log(source_default.green("\u2550".repeat(60)));
117937
+ console.log(source_default.green.bold(" LOCAL ADMINISTRATOR PASSWORD RESET"));
117938
+ console.log(source_default.green("\u2550".repeat(60)));
117939
+ console.log();
117940
+ console.log(source_default.white(" Email: "), source_default.cyan(adminEmail));
117941
+ console.log(source_default.white(" Password: "), source_default.yellow.bold(newPassword));
117942
+ console.log();
117943
+ console.log(source_default.red(" \u26A0 SAVE THIS PASSWORD NOW - IT WILL NOT BE SHOWN AGAIN"));
117944
+ console.log(source_default.green("\u2550".repeat(60)));
117945
+ console.log();
117946
+ } else {
117947
+ await import_mssql.default.connect(connectionString);
117948
+ spinner.text = "Connected. Checking account...";
117949
+ const checkResult = await import_mssql.default.query`
117950
+ SELECT COUNT(*) as count
117951
+ FROM [core].[auth_Users]
117952
+ WHERE Email = ${adminEmail}
117953
+ `;
117954
+ const exists = checkResult.recordset[0].count > 0;
117955
+ if (!exists) {
117956
+ spinner.fail(`Account ${source_default.yellow(adminEmail)} does not exist.`);
117957
+ logger.error("Cannot reset password for non-existent account.");
117958
+ logger.info("Use the application seeding or AdminTool to create the account first.");
117959
+ await import_mssql.default.close();
117960
+ process.exit(1);
117961
+ }
117962
+ spinner.text = "Generating new password...";
117963
+ const newPassword = generatePassword();
117964
+ const passwordHash = await import_bcryptjs.default.hash(newPassword, 10);
117965
+ spinner.text = "Updating password...";
117966
+ await import_mssql.default.query`
117967
+ UPDATE [core].[auth_Users]
117968
+ SET PasswordHash = ${passwordHash},
117969
+ UpdatedAt = ${/* @__PURE__ */ new Date()}
117970
+ WHERE Email = ${adminEmail}
117971
+ `;
117892
117972
  await import_mssql.default.close();
117893
- process.exit(1);
117973
+ spinner.succeed("Password reset successfully!");
117974
+ console.log();
117975
+ console.log(source_default.green("\u2550".repeat(60)));
117976
+ console.log(source_default.green.bold(" LOCAL ADMINISTRATOR PASSWORD RESET"));
117977
+ console.log(source_default.green("\u2550".repeat(60)));
117978
+ console.log();
117979
+ console.log(source_default.white(" Email: "), source_default.cyan(adminEmail));
117980
+ console.log(source_default.white(" Password: "), source_default.yellow.bold(newPassword));
117981
+ console.log();
117982
+ console.log(source_default.red(" \u26A0 SAVE THIS PASSWORD NOW - IT WILL NOT BE SHOWN AGAIN"));
117983
+ console.log(source_default.green("\u2550".repeat(60)));
117984
+ console.log();
117894
117985
  }
117895
- spinner.text = "Generating new password...";
117896
- const newPassword = generatePassword();
117897
- const passwordHash = await import_bcryptjs.default.hash(newPassword, 10);
117898
- spinner.text = "Updating password...";
117899
- await import_mssql.default.query`
117900
- UPDATE [core].[auth_Users]
117901
- SET PasswordHash = ${passwordHash},
117902
- UpdatedAt = ${/* @__PURE__ */ new Date()}
117903
- WHERE Email = ${adminEmail}
117904
- `;
117905
- await import_mssql.default.close();
117906
- spinner.succeed("Password reset successfully!");
117907
- console.log();
117908
- console.log(source_default.green("\u2550".repeat(60)));
117909
- console.log(source_default.green.bold(" LOCAL ADMINISTRATOR PASSWORD RESET"));
117910
- console.log(source_default.green("\u2550".repeat(60)));
117911
- console.log();
117912
- console.log(source_default.white(" Email: "), source_default.cyan(adminEmail));
117913
- console.log(source_default.white(" Password: "), source_default.yellow.bold(newPassword));
117914
- console.log();
117915
- console.log(source_default.red(" \u26A0 SAVE THIS PASSWORD NOW - IT WILL NOT BE SHOWN AGAIN"));
117916
- console.log(source_default.green("\u2550".repeat(60)));
117917
- console.log();
117918
117986
  } catch (error) {
117919
117987
  spinner.fail("Failed to reset password");
117920
117988
  if (error instanceof Error) {
117921
117989
  logger.error(error.message);
117922
117990
  }
117923
- await import_mssql.default.close();
117991
+ if (!connInfo.useWindowsAuth) {
117992
+ await import_mssql.default.close();
117993
+ }
117924
117994
  process.exit(1);
117925
117995
  }
117926
117996
  });