innovationhub-cli 1.1.0 → 2.0.1

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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -86
  3. package/index.js +228 -29
  4. package/package.json +12 -3
  5. package/templates/nest/.env.example +41 -0
  6. package/templates/nest/.prettierrc +4 -0
  7. package/templates/nest/Dockerfile +17 -0
  8. package/templates/nest/README.md +224 -0
  9. package/templates/nest/addons/.github/dependabot.yml +11 -0
  10. package/templates/nest/addons/.github/labeler.yml +34 -0
  11. package/templates/nest/addons/.github/workflows/check-branch.yml +24 -0
  12. package/templates/nest/addons/.github/workflows/ci.yml +29 -0
  13. package/templates/nest/addons/.github/workflows/draft-release.yml +22 -0
  14. package/templates/nest/addons/.github/workflows/pr-labeler.yml +16 -0
  15. package/templates/nest/addons/.github/workflows/release.yml +33 -0
  16. package/templates/nest/addons/.github/workflows/security-audit.yml +17 -0
  17. package/templates/nest/addons/.github/workflows/semantic-pr.yml +31 -0
  18. package/templates/nest/addons/.github/workflows/stale.yml +24 -0
  19. package/templates/nest/addons/.husky/commit-msg +17 -0
  20. package/templates/nest/addons/.husky/pre-commit +1 -0
  21. package/templates/nest/addons/addons.json +17 -0
  22. package/templates/nest/addons/cloudinary/cloudinary.module.ts +11 -0
  23. package/templates/nest/addons/cloudinary/cloudinary.provider.ts +16 -0
  24. package/templates/nest/addons/cloudinary/cloudinary.service.ts +54 -0
  25. package/templates/nest/docker-compose.yml +58 -0
  26. package/templates/nest/eslint.config.mjs +34 -0
  27. package/templates/nest/jest.config.js +17 -0
  28. package/templates/nest/nest-cli.json +12 -0
  29. package/templates/nest/package.json +99 -0
  30. package/templates/nest/src/app.controller.ts +7 -0
  31. package/templates/nest/src/app.module.ts +69 -0
  32. package/templates/nest/src/app.service.ts +4 -0
  33. package/templates/nest/src/auth/auth.controller.ts +97 -0
  34. package/templates/nest/src/auth/auth.module.ts +46 -0
  35. package/templates/nest/src/auth/auth.service.ts +231 -0
  36. package/templates/nest/src/auth/decorators/roles.decorator.ts +5 -0
  37. package/templates/nest/src/auth/dto/change-password.dto.ts +21 -0
  38. package/templates/nest/src/auth/dto/login-response.dto.ts +25 -0
  39. package/templates/nest/src/auth/dto/login.dto.ts +15 -0
  40. package/templates/nest/src/auth/dto/refresh-token.dto.ts +12 -0
  41. package/templates/nest/src/auth/entities/refresh-token.entity.ts +18 -0
  42. package/templates/nest/src/auth/enums/role.enum.ts +4 -0
  43. package/templates/nest/src/auth/guards/jwt-auth.guard.ts +5 -0
  44. package/templates/nest/src/auth/guards/refresh-token.guard.ts +5 -0
  45. package/templates/nest/src/auth/guards/roles.guard.ts +23 -0
  46. package/templates/nest/src/auth/interfaces/jwt-payload.interface.ts +10 -0
  47. package/templates/nest/src/auth/strategies/jwt.strategy.ts +28 -0
  48. package/templates/nest/src/auth/strategies/local.strategy.ts +23 -0
  49. package/templates/nest/src/auth/strategies/refresh-token.strategy.ts +32 -0
  50. package/templates/nest/src/common/base.entity.ts +19 -0
  51. package/templates/nest/src/common/base.repository.ts +79 -0
  52. package/templates/nest/src/common/base.service.ts +28 -0
  53. package/templates/nest/src/common/constants/errors.constants.ts +33 -0
  54. package/templates/nest/src/common/decorators/user.decorator.ts +9 -0
  55. package/templates/nest/src/common/dto/base-query.dto.ts +56 -0
  56. package/templates/nest/src/common/irepository.ts +18 -0
  57. package/templates/nest/src/common/utils/duration.utils.ts +33 -0
  58. package/templates/nest/src/common/utils/pagination.utils.ts +35 -0
  59. package/templates/nest/src/common/utils/slug.utils.ts +14 -0
  60. package/templates/nest/src/common/utils/transform.utils.ts +62 -0
  61. package/templates/nest/src/common/validators/is-date-after.validator.ts +40 -0
  62. package/templates/nest/src/data-source.ts +23 -0
  63. package/templates/nest/src/main.ts +44 -0
  64. package/templates/nest/src/user/dto/create-user.dto.ts +50 -0
  65. package/templates/nest/src/user/dto/query-users.dto.ts +23 -0
  66. package/templates/nest/src/user/dto/update-user.dto.ts +15 -0
  67. package/templates/nest/src/user/entities/user.entity.ts +66 -0
  68. package/templates/nest/src/user/user.controller.ts +172 -0
  69. package/templates/nest/src/user/user.module.ts +15 -0
  70. package/templates/nest/src/user/user.repository.ts +61 -0
  71. package/templates/nest/src/user/user.service.ts +138 -0
  72. package/templates/nest/template.json +5 -0
  73. package/templates/nest/test/jest-e2e.json +12 -0
  74. package/templates/nest/tsconfig.build.json +4 -0
  75. package/templates/nest/tsconfig.json +25 -0
  76. package/templates/python/.env.example +37 -0
  77. package/templates/python/Dockerfile +18 -0
  78. package/templates/python/README.md +219 -0
  79. package/templates/python/addons/.github/dependabot.yml +11 -0
  80. package/templates/python/addons/.github/labeler.yml +29 -0
  81. package/templates/python/addons/.github/workflows/check-branch.yml +24 -0
  82. package/templates/python/addons/.github/workflows/ci.yml +30 -0
  83. package/templates/python/addons/.github/workflows/draft-release.yml +22 -0
  84. package/templates/python/addons/.github/workflows/pr-labeler.yml +16 -0
  85. package/templates/python/addons/.github/workflows/release.yml +30 -0
  86. package/templates/python/addons/.github/workflows/security-audit.yml +21 -0
  87. package/templates/python/addons/.github/workflows/semantic-pr.yml +31 -0
  88. package/templates/python/addons/.github/workflows/stale.yml +24 -0
  89. package/templates/python/addons/addons.json +17 -0
  90. package/templates/python/addons/cloudinary/service.py +67 -0
  91. package/templates/python/addons/pre-commit/.pre-commit-config.yaml +31 -0
  92. package/templates/python/alembic/env.py +56 -0
  93. package/templates/python/alembic/script.py.mako +26 -0
  94. package/templates/python/alembic/versions/.gitkeep +0 -0
  95. package/templates/python/alembic.ini +39 -0
  96. package/templates/python/app/__init__.py +0 -0
  97. package/templates/python/app/auth/__init__.py +5 -0
  98. package/templates/python/app/auth/dependencies.py +118 -0
  99. package/templates/python/app/auth/enums.py +6 -0
  100. package/templates/python/app/auth/models.py +18 -0
  101. package/templates/python/app/auth/router.py +68 -0
  102. package/templates/python/app/auth/schemas.py +58 -0
  103. package/templates/python/app/auth/service.py +180 -0
  104. package/templates/python/app/common/__init__.py +18 -0
  105. package/templates/python/app/common/base_model.py +26 -0
  106. package/templates/python/app/common/base_repository.py +83 -0
  107. package/templates/python/app/common/errors.py +35 -0
  108. package/templates/python/app/common/pagination.py +22 -0
  109. package/templates/python/app/common/schemas.py +20 -0
  110. package/templates/python/app/common/utils.py +15 -0
  111. package/templates/python/app/core/__init__.py +4 -0
  112. package/templates/python/app/core/config.py +55 -0
  113. package/templates/python/app/core/database.py +20 -0
  114. package/templates/python/app/main.py +33 -0
  115. package/templates/python/app/user/__init__.py +4 -0
  116. package/templates/python/app/user/models.py +26 -0
  117. package/templates/python/app/user/repository.py +84 -0
  118. package/templates/python/app/user/router.py +170 -0
  119. package/templates/python/app/user/schemas.py +60 -0
  120. package/templates/python/app/user/service.py +114 -0
  121. package/templates/python/docker-compose.yml +55 -0
  122. package/templates/python/pyproject.toml +46 -0
  123. package/templates/python/requirements-dev.txt +7 -0
  124. package/templates/python/requirements.txt +20 -0
  125. package/templates/python/template.json +5 -0
  126. package/utils/template.js +165 -0
  127. package/utils/git.js +0 -71
@@ -0,0 +1,46 @@
1
+ [project]
2
+ name = "innovation-hub-api"
3
+ version = "0.1.0"
4
+ description = "API backend do Innovation Hub com FastAPI"
5
+ requires-python = ">=3.11"
6
+ dependencies = [
7
+ "fastapi[standard]>=0.115.0",
8
+ "uvicorn[standard]>=0.34.0",
9
+ "sqlalchemy[asyncio]>=2.0.0",
10
+ "asyncpg>=0.30.0",
11
+ "alembic>=1.15.0",
12
+ "pydantic-settings>=2.7.0",
13
+ "python-jose[cryptography]>=3.3.0",
14
+ "passlib[bcrypt]>=1.7.0",
15
+ "python-multipart>=0.0.18",
16
+ "fastapi-mail>=1.4.0",
17
+ "jinja2>=3.1.0",
18
+ ]
19
+
20
+ [project.optional-dependencies]
21
+ dev = [
22
+ "pytest>=8.0.0",
23
+ "pytest-asyncio>=0.24.0",
24
+ "httpx>=0.28.0",
25
+ "ruff>=0.9.0",
26
+ ]
27
+
28
+ [tool.setuptools.packages.find]
29
+ include = ["app*"]
30
+
31
+ [tool.ruff]
32
+ target-version = "py311"
33
+ line-length = 100
34
+
35
+ [tool.ruff.lint]
36
+ select = ["E", "F", "I", "UP", "B", "SIM"]
37
+ ignore = [
38
+ "B008", # Depends() em defaults é o padrão do FastAPI
39
+ ]
40
+
41
+ [tool.ruff.format]
42
+ quote-style = "double"
43
+
44
+ [tool.pytest.ini_options]
45
+ asyncio_mode = "auto"
46
+ testpaths = ["tests"]
@@ -0,0 +1,7 @@
1
+ -r requirements.txt
2
+
3
+ # Dev / Test
4
+ pytest>=8.0.0
5
+ pytest-asyncio>=0.24.0
6
+ httpx>=0.28.0
7
+ ruff>=0.9.0
@@ -0,0 +1,20 @@
1
+ # Core
2
+ fastapi[standard]>=0.115.0
3
+ uvicorn[standard]>=0.34.0
4
+
5
+ # Database
6
+ sqlalchemy[asyncio]>=2.0.0
7
+ asyncpg>=0.30.0
8
+ alembic>=1.15.0
9
+
10
+ # Config
11
+ pydantic-settings>=2.7.0
12
+
13
+ # Auth
14
+ python-jose[cryptography]>=3.3.0
15
+ passlib[bcrypt]>=1.7.0
16
+ python-multipart>=0.0.18
17
+
18
+ # Mail
19
+ fastapi-mail>=1.4.0
20
+ jinja2>=3.1.0
@@ -0,0 +1,5 @@
1
+ {
2
+ "label": "Python (FastAPI)",
3
+ "description": "Backend Python com SQLAlchemy, Alembic, JWT Auth",
4
+ "installCmd": "pip install -r requirements.txt"
5
+ }
@@ -0,0 +1,165 @@
1
+ import * as p from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { execSync } from "node:child_process";
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
10
+
11
+ const IGNORE_ON_COPY = new Set([
12
+ "node_modules",
13
+ "dist",
14
+ "package-lock.json",
15
+ ".ruff_cache",
16
+ ".git",
17
+ "addons",
18
+ "addons.json",
19
+ "template.json",
20
+ "innovation_hub_api.egg-info",
21
+ ]);
22
+
23
+ export async function getAvailableStacks() {
24
+ const entries = await fs.readdir(TEMPLATES_DIR, { withFileTypes: true });
25
+ const stacks = [];
26
+
27
+ for (const entry of entries) {
28
+ if (!entry.isDirectory()) continue;
29
+
30
+ const metaFile = path.join(TEMPLATES_DIR, entry.name, "template.json");
31
+ try {
32
+ const raw = await fs.readFile(metaFile, "utf-8");
33
+ const meta = JSON.parse(raw);
34
+ stacks.push({
35
+ value: entry.name,
36
+ label: meta.label || entry.name,
37
+ hint: meta.description || "",
38
+ installCmd: meta.installCmd || "",
39
+ });
40
+ } catch {
41
+ // template.json não existe, ignorar pasta
42
+ }
43
+ }
44
+
45
+ return stacks;
46
+ }
47
+
48
+ /**
49
+ * Lê o addons.json de uma stack e retorna as opções disponíveis.
50
+ * Retorna array vazio se não existir addons.
51
+ */
52
+ export async function getAvailableAddons(stack) {
53
+ const addonsFile = path.join(TEMPLATES_DIR, stack, "addons", "addons.json");
54
+ try {
55
+ const raw = await fs.readFile(addonsFile, "utf-8");
56
+ return JSON.parse(raw);
57
+ } catch {
58
+ return [];
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Copia o template da stack para a pasta do projeto,
64
+ * instala os addons selecionados e inicializa o git.
65
+ */
66
+ export async function setupProject(stack, projectName, selectedAddons = []) {
67
+ const s = p.spinner();
68
+ s.start(`Preparando base ${stack}...`);
69
+
70
+ try {
71
+ const stackPath = path.join(TEMPLATES_DIR, stack);
72
+
73
+ // Verificar se o template existe
74
+ try {
75
+ await fs.access(stackPath);
76
+ } catch {
77
+ throw new Error(`Template para '${stack}' não encontrado.`);
78
+ }
79
+
80
+ // Copiar template (filtrando arquivos desnecessários)
81
+ await fs.cp(stackPath, projectName, {
82
+ recursive: true,
83
+ filter: (src) => {
84
+ const basename = path.basename(src);
85
+ return !IGNORE_ON_COPY.has(basename);
86
+ },
87
+ });
88
+
89
+ // Instalar addons selecionados
90
+ if (selectedAddons.length > 0) {
91
+ const addonsConfig = await getAvailableAddons(stack);
92
+
93
+ for (let i = 0; i < selectedAddons.length; i++) {
94
+ const addonName = selectedAddons[i];
95
+ const config = addonsConfig.find((a) => a.name === addonName);
96
+ if (!config) continue;
97
+
98
+ s.message(
99
+ `Instalando addon ${i + 1}/${selectedAddons.length}: ${config.label}...`,
100
+ );
101
+
102
+ const addonSource = path.join(stackPath, "addons", addonName);
103
+ try {
104
+ await fs.access(addonSource);
105
+ } catch {
106
+ continue;
107
+ }
108
+
109
+ const addonDest = path.join(projectName, config.dest);
110
+ await fs.mkdir(addonDest, { recursive: true });
111
+ await fs.cp(addonSource, addonDest, { recursive: true });
112
+ }
113
+ }
114
+
115
+ // Gerar .env a partir do .env.example
116
+ const envExample = path.join(projectName, ".env.example");
117
+ const envFile = path.join(projectName, ".env");
118
+ try {
119
+ await fs.access(envExample);
120
+ await fs.copyFile(envExample, envFile);
121
+ s.message("Arquivo .env criado a partir do .env.example");
122
+ } catch {
123
+ // .env.example não existe, seguir sem criar
124
+ }
125
+
126
+ // Inicializar git
127
+ try {
128
+ execSync("git init", { cwd: projectName, stdio: "ignore" });
129
+ execSync("git add -A", { cwd: projectName, stdio: "ignore" });
130
+ execSync(
131
+ 'git commit -m "chore: initial project setup via InnovationHub CLI"',
132
+ { cwd: projectName, stdio: "ignore" },
133
+ );
134
+ } catch {
135
+ // git pode não estar instalado, não é crítico
136
+ }
137
+
138
+ s.stop(pc.green("Projeto configurado com sucesso!"));
139
+ } catch (error) {
140
+ s.stop(pc.red("Erro ao configurar o projeto."));
141
+ p.cancel(error.message);
142
+ process.exit(1);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Instala as dependências do projeto.
148
+ * @param {string} installCmd - Comando de instalação (vem do template.json)
149
+ */
150
+ export async function installDependencies(projectName, installCmd) {
151
+ if (!installCmd) return;
152
+
153
+ p.log.step(`Instalando dependências (${pc.cyan(installCmd)})...\n`);
154
+
155
+ try {
156
+ execSync(installCmd, { cwd: projectName, stdio: "inherit" });
157
+ console.log();
158
+ p.log.success(pc.green("Dependências instaladas!"));
159
+ } catch {
160
+ console.log();
161
+ p.log.warning(
162
+ pc.yellow("Falha ao instalar dependências. Instale manualmente."),
163
+ );
164
+ }
165
+ }
package/utils/git.js DELETED
@@ -1,71 +0,0 @@
1
- import { execa } from "execa";
2
- import * as p from "@clack/prompts";
3
- import pc from "picocolors";
4
- import fs from "node:fs/promises";
5
- import path from "node:path";
6
-
7
- const TEMPLATE_REPO =
8
- "https://github.com/ProHub-Innovation/innovation-templates.git";
9
-
10
- export async function setupProject(stack, projectName, addons = []) {
11
- const tempDir = `.temp-${Date.now()}`;
12
- const s = p.spinner();
13
- s.start(`Preparando base ${stack}...`);
14
-
15
- try {
16
- await execa("git", ["clone", "--depth", "1", TEMPLATE_REPO, tempDir]);
17
- const stackPath = path.join(tempDir, stack);
18
- try {
19
- await fs.access(stackPath);
20
- } catch {
21
- throw new Error( `Template para '${stack}' não encontrado no repositório.`);
22
- }
23
-
24
- await fs.cp(stackPath, projectName, { recursive: true });
25
- if (addons.length > 0) {
26
- s.message("Instalando addons...");
27
- for (const addon of addons) {
28
- const addonSource = path.join(tempDir, "addons", addon);
29
- try {
30
- await fs.access(addonSource);
31
- } catch {
32
- continue;
33
- }
34
-
35
- if (addon === "github") {
36
- const githubDest = path.join(projectName, ".github");
37
- await fs.mkdir(githubDest, { recursive: true });
38
- await fs.cp(addonSource, githubDest, { recursive: true });
39
- } else if (addon === "cloudinary") {
40
- const cloudinaryDest = path.join(projectName, "src/cloudinary");
41
- await fs.mkdir(cloudinaryDest, { recursive: true });
42
- await fs.cp(addonSource, cloudinaryDest, { recursive: true });
43
- } else {
44
- await fs.cp(addonSource, projectName, { recursive: true });
45
- }
46
- }
47
- }
48
- await fs.rm(tempDir, { recursive: true, force: true });
49
- const gitPath = path.join(projectName, ".git");
50
- await fs.rm(gitPath, { recursive: true, force: true }).catch(() => {});
51
- await execa("git", ["init"], { cwd: projectName });
52
-
53
- s.stop(pc.green("Projeto configurado com sucesso!"));
54
-
55
- p.note(
56
- `
57
- Próximos passos:
58
- 1. cd ${projectName}
59
- 2. npm install (ou gerenciador de preferência)
60
- 3. Validar variáveis de ambiente (.env)
61
- `,
62
- "Tudo pronto!",
63
- );
64
- } catch (error) {
65
- await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
66
-
67
- s.stop(pc.red("Erro ao configurar o projeto."));
68
- p.cancel(error.message);
69
- process.exit(1);
70
- }
71
- }