agent-workflow-kit-cli 1.3.5 โ 1.3.6
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/commands/create.js +162 -0
- package/dist/cli/index.js +15 -1
- package/package.json +1 -1
- package/templates/react-ts/project/.gitignore.hbs +24 -0
- package/templates/react-ts/project/eslint.config.js.hbs +34 -0
- package/templates/react-ts/project/index.html.hbs +17 -0
- package/templates/react-ts/project/package.json.hbs +28 -0
- package/templates/react-ts/project/src/App.tsx.hbs +60 -0
- package/templates/react-ts/project/src/components/Counter.tsx.hbs +16 -0
- package/templates/react-ts/project/src/components/ThemeToggle.tsx.hbs +29 -0
- package/templates/react-ts/project/src/hooks/useLocalStorage.ts.hbs +25 -0
- package/templates/react-ts/project/src/main.tsx.hbs +10 -0
- package/templates/react-ts/project/src/services/api.ts.hbs +16 -0
- package/templates/react-ts/project/src/styles/App.css.hbs +143 -0
- package/templates/react-ts/project/src/styles/index.css.hbs +34 -0
- package/templates/react-ts/project/src/vite-env.d.ts.hbs +1 -0
- package/templates/react-ts/project/tsconfig.app.json.hbs +26 -0
- package/templates/react-ts/project/tsconfig.json.hbs +7 -0
- package/templates/react-ts/project/tsconfig.node.json.hbs +24 -0
- package/templates/react-ts/project/vite.config.ts.hbs +7 -0
- package/templates/spring-boot/project/.gitignore.hbs +28 -0
- package/templates/spring-boot/project/pom.xml.hbs +69 -0
- package/templates/spring-boot/project/src/main/java/com/example/packageName/DemoApplication.java.hbs +13 -0
- package/templates/spring-boot/project/src/main/java/com/example/packageName/controller/UserController.java.hbs +37 -0
- package/templates/spring-boot/project/src/main/java/com/example/packageName/dto/UserDTO.java.hbs +16 -0
- package/templates/spring-boot/project/src/main/java/com/example/packageName/entity/User.java.hbs +27 -0
- package/templates/spring-boot/project/src/main/java/com/example/packageName/repository/UserRepository.java.hbs +9 -0
- package/templates/spring-boot/project/src/main/java/com/example/packageName/service/UserService.java.hbs +11 -0
- package/templates/spring-boot/project/src/main/java/com/example/packageName/service/impl/UserServiceImpl.java.hbs +55 -0
- package/templates/spring-boot/project/src/main/resources/application.yml.hbs +20 -0
- package/templates/spring-boot/project/src/test/java/com/example/packageName/DemoApplicationTests.java.hbs +13 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { promises as fs } from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import { runInit } from "./init.js";
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const TEMPLATES_DIR = path.resolve(__dirname, "../../../templates");
|
|
13
|
+
export async function runCreate(template, projectNameInput, options) {
|
|
14
|
+
console.log(chalk.bold.cyan("\n๐ Agent Workflow Kit - Creating New Project..."));
|
|
15
|
+
console.log(chalk.dim("------------------------------------------"));
|
|
16
|
+
const validTemplates = ["spring-boot", "react-ts"];
|
|
17
|
+
if (!validTemplates.includes(template)) {
|
|
18
|
+
console.error(chalk.red(`Error: Invalid template '${template}'. Supported templates are: ${validTemplates.join(", ")}`));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const projectName = projectNameInput || `my-${template}-project`;
|
|
22
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
23
|
+
console.log(`${chalk.bold("Template:")} ${chalk.green(template)}`);
|
|
24
|
+
console.log(`${chalk.bold("Project Name:")} ${chalk.green(projectName)}`);
|
|
25
|
+
console.log(`${chalk.bold("Target Dir:")} ${chalk.green(targetDir)}`);
|
|
26
|
+
console.log(`${chalk.bold("Dry Run:")} ${options.dryRun ? chalk.yellow("Enabled ๐งช") : chalk.gray("Disabled")}`);
|
|
27
|
+
console.log(chalk.dim("------------------------------------------\n"));
|
|
28
|
+
// Verify target directory doesn't exist or is empty
|
|
29
|
+
try {
|
|
30
|
+
const stat = await fs.stat(targetDir);
|
|
31
|
+
if (stat.isDirectory()) {
|
|
32
|
+
const files = await fs.readdir(targetDir);
|
|
33
|
+
if (files.length > 0) {
|
|
34
|
+
console.error(chalk.red(`Error: Directory '${targetDir}' already exists and is not empty.`));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Directory doesn't exist, which is fine
|
|
41
|
+
}
|
|
42
|
+
const templateProjectDir = path.join(TEMPLATES_DIR, template, "project");
|
|
43
|
+
// Verify template project skeleton exists
|
|
44
|
+
try {
|
|
45
|
+
await fs.access(templateProjectDir);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
console.error(chalk.red(`Error: Project template for '${template}' was not found.`));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const safePackageName = projectName
|
|
52
|
+
.toLowerCase()
|
|
53
|
+
.replace(/[^a-z0-9]/g, "");
|
|
54
|
+
if (options.dryRun) {
|
|
55
|
+
console.log(chalk.yellow(`[Dry Run] Would create project directory: ${targetDir}`));
|
|
56
|
+
await listTemplateFilesDry(templateProjectDir, templateProjectDir, targetDir, safePackageName);
|
|
57
|
+
console.log(chalk.bold.green("\n๐ Dry Run completed successfully!"));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Create project directory
|
|
61
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
62
|
+
// Copy and interpolate template files
|
|
63
|
+
await copyAndInterpolate(templateProjectDir, templateProjectDir, targetDir, projectName, safePackageName);
|
|
64
|
+
console.log(chalk.green(`โ๏ธ Project skeleton for ${template} created successfully.`));
|
|
65
|
+
// Initialize agent guidelines in the new project directory
|
|
66
|
+
console.log(chalk.cyan("\nInitializing AI agent guidelines inside project..."));
|
|
67
|
+
const originalCwd = process.cwd();
|
|
68
|
+
try {
|
|
69
|
+
process.chdir(targetDir);
|
|
70
|
+
await runInit({
|
|
71
|
+
stack: template,
|
|
72
|
+
agent: "both",
|
|
73
|
+
dryRun: false,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
console.error(chalk.red(`Warning: Failed to initialize agent guidelines: ${err instanceof Error ? err.message : String(err)}`));
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
process.chdir(originalCwd);
|
|
81
|
+
}
|
|
82
|
+
printCreateSuccess(projectName, template);
|
|
83
|
+
}
|
|
84
|
+
async function listTemplateFilesDry(baseSrc, currentSrc, destDir, safePackageName) {
|
|
85
|
+
const entries = await fs.readdir(currentSrc, { withFileTypes: true });
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
const srcPath = path.join(currentSrc, entry.name);
|
|
88
|
+
// Resolve entry destination name (mapping packageName to safePackageName)
|
|
89
|
+
let destName = entry.name;
|
|
90
|
+
if (entry.name === "packageName") {
|
|
91
|
+
destName = safePackageName;
|
|
92
|
+
}
|
|
93
|
+
const relativePart = path.relative(baseSrc, srcPath);
|
|
94
|
+
let resolvedRelativePart = relativePart
|
|
95
|
+
.replace(/\\/g, "/")
|
|
96
|
+
.replace(/\bpackageName\b/g, safePackageName);
|
|
97
|
+
if (resolvedRelativePart.endsWith(".hbs")) {
|
|
98
|
+
resolvedRelativePart = resolvedRelativePart.slice(0, -4);
|
|
99
|
+
}
|
|
100
|
+
const targetPath = path.join(destDir, resolvedRelativePart);
|
|
101
|
+
if (entry.isDirectory()) {
|
|
102
|
+
console.log(chalk.gray(`[Dry Run] Would create directory: ${targetPath}`));
|
|
103
|
+
await listTemplateFilesDry(baseSrc, srcPath, destDir, safePackageName);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(chalk.gray(`[Dry Run] Would write file: ${targetPath}`));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function copyAndInterpolate(baseSrc, currentSrc, destDir, projectName, safePackageName) {
|
|
111
|
+
const entries = await fs.readdir(currentSrc, { withFileTypes: true });
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const srcPath = path.join(currentSrc, entry.name);
|
|
114
|
+
// Resolve destination path (replaces 'packageName' directory or file parts)
|
|
115
|
+
let destName = entry.name;
|
|
116
|
+
if (entry.name === "packageName") {
|
|
117
|
+
destName = safePackageName;
|
|
118
|
+
}
|
|
119
|
+
const relativePart = path.relative(baseSrc, srcPath);
|
|
120
|
+
let resolvedRelativePart = relativePart
|
|
121
|
+
.replace(/\\/g, "/")
|
|
122
|
+
.replace(/\bpackageName\b/g, safePackageName);
|
|
123
|
+
if (resolvedRelativePart.endsWith(".hbs")) {
|
|
124
|
+
resolvedRelativePart = resolvedRelativePart.slice(0, -4);
|
|
125
|
+
}
|
|
126
|
+
const targetPath = path.join(destDir, resolvedRelativePart);
|
|
127
|
+
if (entry.isDirectory()) {
|
|
128
|
+
await fs.mkdir(targetPath, { recursive: true });
|
|
129
|
+
await copyAndInterpolate(baseSrc, srcPath, destDir, projectName, safePackageName);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
133
|
+
// Interpolate text files
|
|
134
|
+
const content = await fs.readFile(srcPath, "utf8");
|
|
135
|
+
const interpolated = content
|
|
136
|
+
.replace(/\{\{projectName\}\}/g, projectName)
|
|
137
|
+
.replace(/\{\{safePackageName\}\}/g, safePackageName);
|
|
138
|
+
await fs.writeFile(targetPath, interpolated, "utf8");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function printCreateSuccess(projectName, template) {
|
|
143
|
+
console.log(chalk.bold.green(`\n๐ Project '${projectName}' bootstrapped successfully!`));
|
|
144
|
+
console.log(chalk.bold.cyan("\n๐ Next Steps to run/develop:"));
|
|
145
|
+
console.log(chalk.dim("------------------------------------------"));
|
|
146
|
+
console.log(chalk.white(`1. Change directory:`));
|
|
147
|
+
console.log(chalk.cyan(` cd ${projectName}`));
|
|
148
|
+
if (template === "spring-boot") {
|
|
149
|
+
console.log(chalk.white(`2. Build project using Maven:`));
|
|
150
|
+
console.log(chalk.cyan(` mvn clean install`));
|
|
151
|
+
console.log(chalk.white(`3. Run the Spring Boot application:`));
|
|
152
|
+
console.log(chalk.cyan(` mvn spring-boot:run`));
|
|
153
|
+
}
|
|
154
|
+
else if (template === "react-ts") {
|
|
155
|
+
console.log(chalk.white(`2. Install dependencies:`));
|
|
156
|
+
console.log(chalk.cyan(` npm install`));
|
|
157
|
+
console.log(chalk.white(`3. Start the Vite dev server:`));
|
|
158
|
+
console.log(chalk.cyan(` npm run dev`));
|
|
159
|
+
}
|
|
160
|
+
console.log(chalk.white(`4. Start coding! AI guidelines and rules are already configured.`));
|
|
161
|
+
console.log(chalk.dim("------------------------------------------\n"));
|
|
162
|
+
}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { runInit } from "./commands/init.js";
|
|
8
|
+
import { runCreate } from "./commands/create.js";
|
|
8
9
|
import { runUiServer } from "./commands/ui.js";
|
|
9
10
|
import { runAdd } from "./commands/add.js";
|
|
10
11
|
import { runSync } from "./commands/sync.js";
|
|
@@ -20,7 +21,20 @@ export function runCli() {
|
|
|
20
21
|
program
|
|
21
22
|
.name("agent-workflow-kit")
|
|
22
23
|
.description("Generate AI coding workflows/rules/templates for Codex and Antigravity")
|
|
23
|
-
.version("1.3.
|
|
24
|
+
.version("1.3.6");
|
|
25
|
+
program
|
|
26
|
+
.command("create <template> [projectName]")
|
|
27
|
+
.description("Bootstrap a new project structure with AI workflow rules pre-configured")
|
|
28
|
+
.option("--dry-run", "Output actions to console without creating folders", false)
|
|
29
|
+
.action(async (template, projectName, options) => {
|
|
30
|
+
try {
|
|
31
|
+
await runCreate(template, projectName, options);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
console.error(chalk.red(`Error running create: ${err instanceof Error ? err.message : String(err)}`));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
24
38
|
program
|
|
25
39
|
.command("init")
|
|
26
40
|
.description("Initialize agent guidelines and skills for the repository")
|
package/package.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Logs
|
|
2
|
+
logs
|
|
3
|
+
*.log
|
|
4
|
+
npm-debug.log*
|
|
5
|
+
yarn-debug.log*
|
|
6
|
+
yarn-error.log*
|
|
7
|
+
pnpm-debug.log*
|
|
8
|
+
lerna-debug.log*
|
|
9
|
+
|
|
10
|
+
node_modules
|
|
11
|
+
dist
|
|
12
|
+
dist-ssr
|
|
13
|
+
*.local
|
|
14
|
+
|
|
15
|
+
# Editor directories and files
|
|
16
|
+
.vscode/*
|
|
17
|
+
!.vscode/extensions.json
|
|
18
|
+
.idea
|
|
19
|
+
.DS_Store
|
|
20
|
+
*.suo
|
|
21
|
+
*.ntvs*
|
|
22
|
+
*.njsproj
|
|
23
|
+
*.sln
|
|
24
|
+
*.sw?
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
+
import tseslint from '@typescript-eslint/eslint-plugin'
|
|
6
|
+
import tsparser from '@typescript-eslint/parser'
|
|
7
|
+
|
|
8
|
+
export default [
|
|
9
|
+
{ ignores: ['dist'] },
|
|
10
|
+
{
|
|
11
|
+
files: ['**/*.{ts,tsx}'],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
ecmaVersion: 2020,
|
|
14
|
+
globals: globals.browser,
|
|
15
|
+
parser: tsparser,
|
|
16
|
+
parserOptions: {
|
|
17
|
+
project: ['./tsconfig.app.json', './tsconfig.node.json'],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
plugins: {
|
|
21
|
+
'react-hooks': reactHooks,
|
|
22
|
+
'react-refresh': reactRefresh,
|
|
23
|
+
'@typescript-eslint': tseslint,
|
|
24
|
+
},
|
|
25
|
+
rules: {
|
|
26
|
+
...js.configs.recommended.rules,
|
|
27
|
+
...tseslint.configs.recommended.rules,
|
|
28
|
+
'react-refresh/only-export-components': [
|
|
29
|
+
'warn',
|
|
30
|
+
{ allowConstantExport: true },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>โ๏ธ</text></svg>" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>{{projectName}}</title>
|
|
8
|
+
<!-- Modern Outfit typography from Google Fonts -->
|
|
9
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="root"></div>
|
|
15
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build",
|
|
9
|
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"react": "^18.3.1",
|
|
14
|
+
"react-dom": "^18.3.1"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/react": "^18.3.12",
|
|
18
|
+
"@types/react-dom": "^18.3.1",
|
|
19
|
+
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
|
20
|
+
"@typescript-eslint/parser": "^7.15.0",
|
|
21
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
22
|
+
"eslint": "^8.57.0",
|
|
23
|
+
"eslint-plugin-react-hooks": "^4.6.2",
|
|
24
|
+
"eslint-plugin-react-refresh": "^0.4.7",
|
|
25
|
+
"typescript": "^5.5.2",
|
|
26
|
+
"vite": "^5.4.11"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { Counter } from './components/Counter'
|
|
3
|
+
import { ThemeToggle } from './components/ThemeToggle'
|
|
4
|
+
import { fetchSystemStatus, SystemStatus } from './services/api'
|
|
5
|
+
import './styles/App.css'
|
|
6
|
+
|
|
7
|
+
function App() {
|
|
8
|
+
const [systemStatus, setSystemStatus] = useState<SystemStatus | null>(null)
|
|
9
|
+
const [loading, setLoading] = useState(true)
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
fetchSystemStatus()
|
|
13
|
+
.then((status) => {
|
|
14
|
+
setSystemStatus(status)
|
|
15
|
+
setLoading(false)
|
|
16
|
+
})
|
|
17
|
+
.catch((err) => {
|
|
18
|
+
console.error('Failed to load system status:', err)
|
|
19
|
+
setLoading(false)
|
|
20
|
+
})
|
|
21
|
+
}, [])
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="container">
|
|
25
|
+
<div className="logo-container">
|
|
26
|
+
<span className="logo react" style={{ fontSize: '4rem', userSelect: 'none' }}>โ๏ธ</span>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<h1 className="title">{{projectName}}</h1>
|
|
30
|
+
<p className="subtitle">
|
|
31
|
+
Your AI-Ready React, Vite, and TypeScript project generated with Agent Workflow Kit.
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
{/* Standalone Interactive Component */}
|
|
35
|
+
<Counter />
|
|
36
|
+
|
|
37
|
+
{/* Component utilizing custom hooks */}
|
|
38
|
+
<ThemeToggle />
|
|
39
|
+
|
|
40
|
+
{/* Service layer display */}
|
|
41
|
+
<div className="api-status-card">
|
|
42
|
+
{loading ? (
|
|
43
|
+
<span>Loading system API status...</span>
|
|
44
|
+
) : systemStatus ? (
|
|
45
|
+
<div>
|
|
46
|
+
API Status: <span className="status-badge online">{systemStatus.status}</span> (v{systemStatus.version})
|
|
47
|
+
</div>
|
|
48
|
+
) : (
|
|
49
|
+
<span>API Status: <span className="status-badge" style={{ backgroundColor: 'rgba(239, 68, 68, 0.15)', color: '#f87171' }}>OFFLINE</span></span>
|
|
50
|
+
)}
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<p className="footer">
|
|
54
|
+
Workflow guidelines and skills are configured in the <code>.agents/</code> directory.
|
|
55
|
+
</p>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default App
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export function Counter() {
|
|
4
|
+
const [count, setCount] = useState(0)
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<div className="card">
|
|
8
|
+
<button className="counter-btn" onClick={() => setCount((c) => c + 1)}>
|
|
9
|
+
Count is {count}
|
|
10
|
+
</button>
|
|
11
|
+
<p className="instruction">
|
|
12
|
+
Edit <code>src/components/Counter.tsx</code> to test stateful hot updates.
|
|
13
|
+
</p>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { useLocalStorage } from '../hooks/useLocalStorage'
|
|
3
|
+
|
|
4
|
+
export function ThemeToggle() {
|
|
5
|
+
const [theme, setTheme] = useLocalStorage<'light' | 'dark'>('theme', 'dark')
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
document.documentElement.setAttribute('data-theme', theme)
|
|
9
|
+
if (theme === 'light') {
|
|
10
|
+
document.body.style.backgroundColor = '#f8fafc'
|
|
11
|
+
document.body.style.color = '#0f172a'
|
|
12
|
+
} else {
|
|
13
|
+
document.body.style.backgroundColor = '#0b0f19'
|
|
14
|
+
document.body.style.color = '#e2e8f0'
|
|
15
|
+
}
|
|
16
|
+
}, [theme])
|
|
17
|
+
|
|
18
|
+
const toggleTheme = () => {
|
|
19
|
+
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="theme-toggle-container">
|
|
24
|
+
<button className="theme-btn" onClick={toggleTheme}>
|
|
25
|
+
{theme === 'light' ? '๐ Dark Mode' : 'โ๏ธ Light Mode'}
|
|
26
|
+
</button>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useState, Dispatch, SetStateAction } from 'react'
|
|
2
|
+
|
|
3
|
+
export function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>] {
|
|
4
|
+
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
5
|
+
try {
|
|
6
|
+
const item = window.localStorage.getItem(key)
|
|
7
|
+
return item ? JSON.parse(item) : initialValue
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.warn(`Error reading localStorage key "${key}":`, error)
|
|
10
|
+
return initialValue
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const setValue: Dispatch<SetStateAction<T>> = (value) => {
|
|
15
|
+
try {
|
|
16
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value
|
|
17
|
+
setStoredValue(valueToStore)
|
|
18
|
+
window.localStorage.setItem(key, JSON.stringify(valueToStore))
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.warn(`Error setting localStorage key "${key}":`, error)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return [storedValue, setValue]
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SystemStatus {
|
|
2
|
+
status: string;
|
|
3
|
+
version: string;
|
|
4
|
+
uptime: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function fetchSystemStatus(): Promise<SystemStatus> {
|
|
8
|
+
// Mock asynchronous API call delay
|
|
9
|
+
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
status: 'ONLINE',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
uptime: Math.floor(process.uptime ? process.uptime() : performance.now() / 1000)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: center;
|
|
5
|
+
justify-content: center;
|
|
6
|
+
gap: 1.5rem;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.logo-container {
|
|
10
|
+
display: flex;
|
|
11
|
+
gap: 2rem;
|
|
12
|
+
margin-bottom: 0.5rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.logo {
|
|
16
|
+
font-size: 4rem;
|
|
17
|
+
user-select: none;
|
|
18
|
+
display: inline-block;
|
|
19
|
+
transition: transform 0.3s;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.logo:hover {
|
|
23
|
+
transform: scale(1.15) rotate(10deg);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.title {
|
|
27
|
+
font-size: 3.2rem;
|
|
28
|
+
line-height: 1.1;
|
|
29
|
+
font-weight: 800;
|
|
30
|
+
margin: 0;
|
|
31
|
+
background: linear-gradient(135deg, #a5b4fc, #6366f1, #ec4899);
|
|
32
|
+
-webkit-background-clip: text;
|
|
33
|
+
-webkit-text-fill-color: transparent;
|
|
34
|
+
background-clip: text;
|
|
35
|
+
text-shadow: 0 4px 20px rgba(99, 102, 241, 0.15);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.subtitle {
|
|
39
|
+
font-size: 1.25rem;
|
|
40
|
+
color: #94a3b8;
|
|
41
|
+
max-width: 600px;
|
|
42
|
+
margin: 0 auto;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.card {
|
|
46
|
+
padding: 2.5rem;
|
|
47
|
+
background: rgba(30, 41, 59, 0.4);
|
|
48
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
49
|
+
border-radius: 1.5rem;
|
|
50
|
+
backdrop-filter: blur(16px);
|
|
51
|
+
-webkit-backdrop-filter: blur(16px);
|
|
52
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
|
53
|
+
transition: border-color 0.3s, box-shadow 0.3s, transform 0.3s;
|
|
54
|
+
max-width: 480px;
|
|
55
|
+
width: 100%;
|
|
56
|
+
box-sizing: border-box;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.card:hover {
|
|
60
|
+
border-color: rgba(99, 102, 241, 0.4);
|
|
61
|
+
box-shadow: 0 15px 35px rgba(99, 102, 241, 0.1);
|
|
62
|
+
transform: translateY(-4px);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.counter-btn {
|
|
66
|
+
background: linear-gradient(135deg, #4f46e5, #6366f1);
|
|
67
|
+
color: white;
|
|
68
|
+
border: none;
|
|
69
|
+
padding: 0.75rem 2rem;
|
|
70
|
+
font-size: 1rem;
|
|
71
|
+
font-weight: 600;
|
|
72
|
+
border-radius: 9999px;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
75
|
+
font-family: inherit;
|
|
76
|
+
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.counter-btn:hover {
|
|
80
|
+
transform: scale(1.05);
|
|
81
|
+
box-shadow: 0 6px 16px rgba(79, 70, 229, 0.5);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.theme-toggle-container {
|
|
85
|
+
margin-top: 1rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.theme-btn {
|
|
89
|
+
background: rgba(255, 255, 255, 0.05);
|
|
90
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
91
|
+
color: inherit;
|
|
92
|
+
padding: 0.5rem 1.25rem;
|
|
93
|
+
border-radius: 0.5rem;
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
transition: background 0.2s, border-color 0.2s;
|
|
96
|
+
font-family: inherit;
|
|
97
|
+
font-size: 0.9rem;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.theme-btn:hover {
|
|
101
|
+
background: rgba(255, 255, 255, 0.1);
|
|
102
|
+
border-color: rgba(255, 255, 255, 0.2);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.api-status-card {
|
|
106
|
+
margin-top: 1rem;
|
|
107
|
+
font-size: 0.9rem;
|
|
108
|
+
color: #94a3b8;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.status-badge {
|
|
112
|
+
display: inline-block;
|
|
113
|
+
padding: 0.25rem 0.75rem;
|
|
114
|
+
border-radius: 9999px;
|
|
115
|
+
font-size: 0.8rem;
|
|
116
|
+
font-weight: bold;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.status-badge.online {
|
|
120
|
+
background-color: rgba(34, 197, 94, 0.15);
|
|
121
|
+
color: #4ade80;
|
|
122
|
+
border: 1px solid rgba(34, 197, 94, 0.3);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.instruction {
|
|
126
|
+
margin-top: 1.5rem;
|
|
127
|
+
font-size: 0.875rem;
|
|
128
|
+
color: #64748b;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.instruction code {
|
|
132
|
+
background: rgba(15, 23, 42, 0.6);
|
|
133
|
+
padding: 0.2rem 0.5rem;
|
|
134
|
+
border-radius: 0.25rem;
|
|
135
|
+
font-family: monospace;
|
|
136
|
+
color: #a5b4fc;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.footer {
|
|
140
|
+
font-size: 0.875rem;
|
|
141
|
+
color: #475569;
|
|
142
|
+
margin-top: 2rem;
|
|
143
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
|
3
|
+
line-height: 1.5;
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
|
|
6
|
+
color-scheme: dark;
|
|
7
|
+
color: #e2e8f0;
|
|
8
|
+
background-color: #0b0f19;
|
|
9
|
+
|
|
10
|
+
font-synthesis: none;
|
|
11
|
+
text-rendering: optimizeLegibility;
|
|
12
|
+
-webkit-font-smoothing: antialiased;
|
|
13
|
+
-moz-osx-font-smoothing: grayscale;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
body {
|
|
17
|
+
margin: 0;
|
|
18
|
+
display: flex;
|
|
19
|
+
place-items: center;
|
|
20
|
+
min-width: 320px;
|
|
21
|
+
min-height: 100vh;
|
|
22
|
+
background: radial-gradient(circle at top right, rgba(99, 102, 241, 0.15), transparent 40%),
|
|
23
|
+
radial-gradient(circle at bottom left, rgba(236, 72, 153, 0.12), transparent 40%),
|
|
24
|
+
#070a13;
|
|
25
|
+
transition: background-color 0.3s, color 0.3s;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#root {
|
|
29
|
+
max-width: 1280px;
|
|
30
|
+
margin: 0 auto;
|
|
31
|
+
padding: 2rem;
|
|
32
|
+
text-align: center;
|
|
33
|
+
width: 100%;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"composite": true,
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
5
|
+
"target": "ES2020",
|
|
6
|
+
"useDefineForClassFields": true,
|
|
7
|
+
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
|
8
|
+
"module": "ESNext",
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
|
|
11
|
+
/* Bundler mode */
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
|
|
19
|
+
/* Linting */
|
|
20
|
+
"strict": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"noFallthroughCasesInSwitch": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["src"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"composite": true,
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
5
|
+
"target": "ES2022",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"noFallthroughCasesInSwitch": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["vite.config.ts"]
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
target/
|
|
2
|
+
!.mvn/wrapper/maven-wrapper.jar
|
|
3
|
+
!**/src/main/resources/components/
|
|
4
|
+
|
|
5
|
+
### STS ###
|
|
6
|
+
.apt_generated
|
|
7
|
+
.classpath
|
|
8
|
+
.factorypath
|
|
9
|
+
.project
|
|
10
|
+
.settings
|
|
11
|
+
.springBeans
|
|
12
|
+
.sts4-cache
|
|
13
|
+
|
|
14
|
+
### IntelliJ IDEA ###
|
|
15
|
+
.idea
|
|
16
|
+
*.iws
|
|
17
|
+
*.iml
|
|
18
|
+
*.ipr
|
|
19
|
+
|
|
20
|
+
### NetBeans ###
|
|
21
|
+
/nbproject/private/
|
|
22
|
+
/nbbuild/
|
|
23
|
+
/dist/
|
|
24
|
+
/nbdist/
|
|
25
|
+
/.nb-gradle/
|
|
26
|
+
|
|
27
|
+
### VS Code ###
|
|
28
|
+
.vscode/
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
3
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
4
|
+
<modelVersion>4.0.0</modelVersion>
|
|
5
|
+
<parent>
|
|
6
|
+
<groupId>org.springframework.boot</groupId>
|
|
7
|
+
<artifactId>spring-boot-starter-parent</artifactId>
|
|
8
|
+
<version>3.3.5</version>
|
|
9
|
+
<relativePath/> <!-- lookup parent from repository -->
|
|
10
|
+
</parent>
|
|
11
|
+
<groupId>com.example</groupId>
|
|
12
|
+
<artifactId>{{projectName}}</artifactId>
|
|
13
|
+
<version>0.0.1-SNAPSHOT</version>
|
|
14
|
+
<name>{{projectName}}</name>
|
|
15
|
+
<description>Demo project for Spring Boot and Agent Workflow Kit</description>
|
|
16
|
+
<properties>
|
|
17
|
+
<java.version>17</java.version>
|
|
18
|
+
</properties>
|
|
19
|
+
<dependencies>
|
|
20
|
+
<dependency>
|
|
21
|
+
<groupId>org.springframework.boot</groupId>
|
|
22
|
+
<artifactId>spring-boot-starter-web</artifactId>
|
|
23
|
+
</dependency>
|
|
24
|
+
<dependency>
|
|
25
|
+
<groupId>org.springframework.boot</groupId>
|
|
26
|
+
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
|
27
|
+
</dependency>
|
|
28
|
+
<dependency>
|
|
29
|
+
<groupId>com.h2database</groupId>
|
|
30
|
+
<artifactId>h2</artifactId>
|
|
31
|
+
<scope>runtime</scope>
|
|
32
|
+
</dependency>
|
|
33
|
+
|
|
34
|
+
<dependency>
|
|
35
|
+
<groupId>org.springframework.boot</groupId>
|
|
36
|
+
<artifactId>spring-boot-devtools</artifactId>
|
|
37
|
+
<scope>runtime</scope>
|
|
38
|
+
<optional>true</optional>
|
|
39
|
+
</dependency>
|
|
40
|
+
<dependency>
|
|
41
|
+
<groupId>org.projectlombok</groupId>
|
|
42
|
+
<artifactId>lombok</artifactId>
|
|
43
|
+
<optional>true</optional>
|
|
44
|
+
</dependency>
|
|
45
|
+
<dependency>
|
|
46
|
+
<groupId>org.springframework.boot</groupId>
|
|
47
|
+
<artifactId>spring-boot-starter-test</artifactId>
|
|
48
|
+
<scope>test</scope>
|
|
49
|
+
</dependency>
|
|
50
|
+
</dependencies>
|
|
51
|
+
|
|
52
|
+
<build>
|
|
53
|
+
<plugins>
|
|
54
|
+
<plugin>
|
|
55
|
+
<groupId>org.springframework.boot</groupId>
|
|
56
|
+
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
57
|
+
<configuration>
|
|
58
|
+
<excludes>
|
|
59
|
+
<exclude>
|
|
60
|
+
<groupId>org.projectlombok</groupId>
|
|
61
|
+
<artifactId>lombok</artifactId>
|
|
62
|
+
</exclude>
|
|
63
|
+
</excludes>
|
|
64
|
+
</configuration>
|
|
65
|
+
</plugin>
|
|
66
|
+
</plugins>
|
|
67
|
+
</build>
|
|
68
|
+
|
|
69
|
+
</project>
|
package/templates/spring-boot/project/src/main/java/com/example/packageName/DemoApplication.java.hbs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
package com.example.{{safePackageName}};
|
|
2
|
+
|
|
3
|
+
import org.springframework.boot.SpringApplication;
|
|
4
|
+
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
5
|
+
|
|
6
|
+
@SpringBootApplication
|
|
7
|
+
public class DemoApplication {
|
|
8
|
+
|
|
9
|
+
public static void main(String[] args) {
|
|
10
|
+
SpringApplication.run(DemoApplication.class, args);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package com.example.{{safePackageName}}.controller;
|
|
2
|
+
|
|
3
|
+
import com.example.{{safePackageName}}.dto.UserDTO;
|
|
4
|
+
import com.example.{{safePackageName}}.service.UserService;
|
|
5
|
+
import lombok.RequiredArgsConstructor;
|
|
6
|
+
import org.springframework.http.ResponseEntity;
|
|
7
|
+
import org.springframework.web.bind.annotation.GetMapping;
|
|
8
|
+
import org.springframework.web.bind.annotation.PathVariable;
|
|
9
|
+
import org.springframework.web.bind.annotation.PostMapping;
|
|
10
|
+
import org.springframework.web.bind.annotation.RequestBody;
|
|
11
|
+
import org.springframework.web.bind.annotation.RequestMapping;
|
|
12
|
+
import org.springframework.web.bind.annotation.RestController;
|
|
13
|
+
|
|
14
|
+
import java.util.List;
|
|
15
|
+
|
|
16
|
+
@RestController
|
|
17
|
+
@RequestMapping("/api/users")
|
|
18
|
+
@RequiredArgsConstructor
|
|
19
|
+
public class UserController {
|
|
20
|
+
|
|
21
|
+
private final UserService userService;
|
|
22
|
+
|
|
23
|
+
@GetMapping
|
|
24
|
+
public ResponseEntity<List<UserDTO>> getAllUsers() {
|
|
25
|
+
return ResponseEntity.ok(userService.getAllUsers());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@PostMapping
|
|
29
|
+
public ResponseEntity<UserDTO> createUser(@RequestBody UserDTO userDTO) {
|
|
30
|
+
return ResponseEntity.ok(userService.createUser(userDTO));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@GetMapping("/{id}")
|
|
34
|
+
public ResponseEntity<UserDTO> getUserById(@PathVariable Long id) {
|
|
35
|
+
return ResponseEntity.ok(userService.getUserById(id));
|
|
36
|
+
}
|
|
37
|
+
}
|
package/templates/spring-boot/project/src/main/java/com/example/packageName/dto/UserDTO.java.hbs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package com.example.{{safePackageName}}.dto;
|
|
2
|
+
|
|
3
|
+
import lombok.AllArgsConstructor;
|
|
4
|
+
import lombok.Builder;
|
|
5
|
+
import lombok.Data;
|
|
6
|
+
import lombok.NoArgsConstructor;
|
|
7
|
+
|
|
8
|
+
@Data
|
|
9
|
+
@Builder
|
|
10
|
+
@NoArgsConstructor
|
|
11
|
+
@AllArgsConstructor
|
|
12
|
+
public class UserDTO {
|
|
13
|
+
private Long id;
|
|
14
|
+
private String name;
|
|
15
|
+
private String email;
|
|
16
|
+
}
|
package/templates/spring-boot/project/src/main/java/com/example/packageName/entity/User.java.hbs
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package com.example.{{safePackageName}}.entity;
|
|
2
|
+
|
|
3
|
+
import jakarta.persistence.Entity;
|
|
4
|
+
import jakarta.persistence.GeneratedValue;
|
|
5
|
+
import jakarta.persistence.GenerationType;
|
|
6
|
+
import jakarta.persistence.Id;
|
|
7
|
+
import jakarta.persistence.Table;
|
|
8
|
+
import lombok.AllArgsConstructor;
|
|
9
|
+
import lombok.Builder;
|
|
10
|
+
import lombok.Data;
|
|
11
|
+
import lombok.NoArgsConstructor;
|
|
12
|
+
|
|
13
|
+
@Entity
|
|
14
|
+
@Table(name = "users")
|
|
15
|
+
@Data
|
|
16
|
+
@Builder
|
|
17
|
+
@NoArgsConstructor
|
|
18
|
+
@AllArgsConstructor
|
|
19
|
+
public class User {
|
|
20
|
+
|
|
21
|
+
@Id
|
|
22
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
23
|
+
private Long id;
|
|
24
|
+
|
|
25
|
+
private String name;
|
|
26
|
+
private String email;
|
|
27
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
package com.example.{{safePackageName}}.repository;
|
|
2
|
+
|
|
3
|
+
import com.example.{{safePackageName}}.entity.User;
|
|
4
|
+
import org.springframework.data.jpa.repository.JpaRepository;
|
|
5
|
+
import org.springframework.stereotype.Repository;
|
|
6
|
+
|
|
7
|
+
@Repository
|
|
8
|
+
public interface UserRepository extends JpaRepository<User, Long> {
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
package com.example.{{safePackageName}}.service;
|
|
2
|
+
|
|
3
|
+
import com.example.{{safePackageName}}.dto.UserDTO;
|
|
4
|
+
|
|
5
|
+
import java.util.List;
|
|
6
|
+
|
|
7
|
+
public interface UserService {
|
|
8
|
+
List<UserDTO> getAllUsers();
|
|
9
|
+
UserDTO createUser(UserDTO userDTO);
|
|
10
|
+
UserDTO getUserById(Long id);
|
|
11
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package com.example.{{safePackageName}}.service.impl;
|
|
2
|
+
|
|
3
|
+
import com.example.{{safePackageName}}.dto.UserDTO;
|
|
4
|
+
import com.example.{{safePackageName}}.entity.User;
|
|
5
|
+
import com.example.{{safePackageName}}.repository.UserRepository;
|
|
6
|
+
import com.example.{{safePackageName}}.service.UserService;
|
|
7
|
+
import lombok.RequiredArgsConstructor;
|
|
8
|
+
import org.springframework.stereotype.Service;
|
|
9
|
+
|
|
10
|
+
import java.util.List;
|
|
11
|
+
import java.util.stream.Collectors;
|
|
12
|
+
|
|
13
|
+
@Service
|
|
14
|
+
@RequiredArgsConstructor
|
|
15
|
+
public class UserServiceImpl implements UserService {
|
|
16
|
+
|
|
17
|
+
private final UserRepository userRepository;
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public List<UserDTO> getAllUsers() {
|
|
21
|
+
return userRepository.findAll().stream()
|
|
22
|
+
.map(this::convertToDTO)
|
|
23
|
+
.collect(Collectors.toList());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Override
|
|
27
|
+
public UserDTO createUser(UserDTO userDTO) {
|
|
28
|
+
User user = convertToEntity(userDTO);
|
|
29
|
+
User savedUser = userRepository.save(user);
|
|
30
|
+
return convertToDTO(savedUser);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Override
|
|
34
|
+
public UserDTO getUserById(Long id) {
|
|
35
|
+
User user = userRepository.findById(id)
|
|
36
|
+
.orElseThrow(() -> new RuntimeException("User not found with id: " + id));
|
|
37
|
+
return convertToDTO(user);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private UserDTO convertToDTO(User user) {
|
|
41
|
+
return UserDTO.builder()
|
|
42
|
+
.id(user.getId())
|
|
43
|
+
.name(user.getName())
|
|
44
|
+
.email(user.getEmail())
|
|
45
|
+
.build();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private User convertToEntity(UserDTO userDTO) {
|
|
49
|
+
return User.builder()
|
|
50
|
+
.id(userDTO.getId())
|
|
51
|
+
.name(userDTO.getName())
|
|
52
|
+
.email(userDTO.getEmail())
|
|
53
|
+
.build();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
server:
|
|
2
|
+
port: 8080
|
|
3
|
+
|
|
4
|
+
spring:
|
|
5
|
+
application:
|
|
6
|
+
name: {{projectName}}
|
|
7
|
+
datasource:
|
|
8
|
+
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
|
|
9
|
+
driver-class-name: org.h2.Driver
|
|
10
|
+
username: sa
|
|
11
|
+
password: password
|
|
12
|
+
h2:
|
|
13
|
+
console:
|
|
14
|
+
enabled: true
|
|
15
|
+
path: /h2-console
|
|
16
|
+
jpa:
|
|
17
|
+
database-platform: org.hibernate.dialect.H2Dialect
|
|
18
|
+
hibernate:
|
|
19
|
+
ddl-auto: update
|
|
20
|
+
show-sql: true
|