create-backlist 6.0.6 → 6.0.8
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/bin/index.js +141 -0
- package/package.json +4 -10
- package/src/analyzer.js +105 -308
- package/src/generators/dotnet.js +94 -120
- package/src/generators/java.js +109 -157
- package/src/generators/node.js +85 -262
- package/src/generators/template.js +2 -38
- package/src/templates/dotnet/partials/Controller.cs.ejs +14 -7
- package/src/templates/java-spring/partials/ApplicationSeeder.java.ejs +2 -7
- package/src/templates/java-spring/partials/AuthController.java.ejs +10 -23
- package/src/templates/java-spring/partials/Controller.java.ejs +6 -17
- package/src/templates/java-spring/partials/Dockerfile.ejs +1 -6
- package/src/templates/java-spring/partials/Entity.java.ejs +5 -15
- package/src/templates/java-spring/partials/JwtAuthFilter.java.ejs +7 -30
- package/src/templates/java-spring/partials/JwtService.java.ejs +10 -38
- package/src/templates/java-spring/partials/Repository.java.ejs +1 -10
- package/src/templates/java-spring/partials/Service.java.ejs +7 -45
- package/src/templates/java-spring/partials/User.java.ejs +4 -17
- package/src/templates/java-spring/partials/UserDetailsServiceImpl.java.ejs +4 -10
- package/src/templates/java-spring/partials/UserRepository.java.ejs +0 -8
- package/src/templates/java-spring/partials/docker-compose.yml.ejs +8 -16
- package/src/templates/node-ts-express/base/server.ts +6 -13
- package/src/templates/node-ts-express/base/tsconfig.json +3 -13
- package/src/templates/node-ts-express/partials/ApiDocs.ts.ejs +7 -17
- package/src/templates/node-ts-express/partials/App.test.ts.ejs +26 -49
- package/src/templates/node-ts-express/partials/Auth.controller.ts.ejs +62 -56
- package/src/templates/node-ts-express/partials/Auth.middleware.ts.ejs +10 -21
- package/src/templates/node-ts-express/partials/Controller.ts.ejs +40 -40
- package/src/templates/node-ts-express/partials/DbContext.cs.ejs +3 -3
- package/src/templates/node-ts-express/partials/Dockerfile.ejs +11 -9
- package/src/templates/node-ts-express/partials/Model.cs.ejs +7 -25
- package/src/templates/node-ts-express/partials/Model.ts.ejs +12 -20
- package/src/templates/node-ts-express/partials/PrismaController.ts.ejs +55 -72
- package/src/templates/node-ts-express/partials/PrismaSchema.prisma.ejs +12 -27
- package/src/templates/node-ts-express/partials/README.md.ejs +12 -9
- package/src/templates/node-ts-express/partials/Seeder.ts.ejs +64 -44
- package/src/templates/node-ts-express/partials/docker-compose.yml.ejs +16 -31
- package/src/templates/node-ts-express/partials/package.json.ejs +1 -3
- package/src/templates/node-ts-express/partials/routes.ts.ejs +24 -35
- package/src/utils.js +4 -19
- package/bin/backlist.js +0 -227
- package/src/db/prisma.ts +0 -4
- package/src/scanner/analyzeFrontend.js +0 -146
- package/src/scanner/index.js +0 -99
- package/src/templates/dotnet/partials/Dto.cs.ejs +0 -8
- package/src/templates/node-ts-express/partials/prismaClient.ts.ejs +0 -4
|
@@ -4,32 +4,23 @@ package <%= group %>.<%= projectName %>.controller;
|
|
|
4
4
|
import org.springframework.http.ResponseEntity;
|
|
5
5
|
import org.springframework.http.HttpStatus;
|
|
6
6
|
import org.springframework.web.bind.annotation.*;
|
|
7
|
-
|
|
8
7
|
import java.util.List;
|
|
9
|
-
|
|
8
|
+
import java.util.Optional;
|
|
10
9
|
import <%= group %>.<%= projectName %>.service.<%= controllerName %>Service;
|
|
11
10
|
import <%= group %>.<%= projectName %>.model.<%= controllerName %>;
|
|
12
11
|
|
|
13
12
|
@RestController
|
|
14
|
-
@RequestMapping("
|
|
13
|
+
@RequestMapping("/api/<%= controllerName.toLowerCase() %>s")
|
|
15
14
|
@CrossOrigin(origins = "*")
|
|
16
15
|
public class <%= controllerName %>Controller {
|
|
17
16
|
private final <%= controllerName %>Service service;
|
|
17
|
+
public <%= controllerName %>Controller(<%= controllerName %>Service service) { this.service = service; }
|
|
18
18
|
|
|
19
|
-
public
|
|
20
|
-
this.service = service;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
@GetMapping
|
|
24
|
-
public List<<%= controllerName %>> all() {
|
|
25
|
-
return service.findAll();
|
|
26
|
-
}
|
|
19
|
+
@GetMapping public List<<%= controllerName %>> all() { return service.findAll(); }
|
|
27
20
|
|
|
28
21
|
@GetMapping("/{id}")
|
|
29
22
|
public ResponseEntity<<%= controllerName %>> one(@PathVariable Long id) {
|
|
30
|
-
return service.findById(id)
|
|
31
|
-
.map(ResponseEntity::ok)
|
|
32
|
-
.orElse(ResponseEntity.notFound().build());
|
|
23
|
+
return service.findById(id).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
|
|
33
24
|
}
|
|
34
25
|
|
|
35
26
|
@PostMapping
|
|
@@ -39,9 +30,7 @@ public class <%= controllerName %>Controller {
|
|
|
39
30
|
|
|
40
31
|
@PutMapping("/{id}")
|
|
41
32
|
public ResponseEntity<<%= controllerName %>> update(@PathVariable Long id, @RequestBody <%= controllerName %> m) {
|
|
42
|
-
return service.update(id, m)
|
|
43
|
-
.map(ResponseEntity::ok)
|
|
44
|
-
.orElse(ResponseEntity.notFound().build());
|
|
33
|
+
return service.update(id, m).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
|
|
45
34
|
}
|
|
46
35
|
|
|
47
36
|
@DeleteMapping("/{id}")
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
# Auto-generated by create-backlist
|
|
2
2
|
FROM eclipse-temurin:21-jdk AS build
|
|
3
3
|
WORKDIR /app
|
|
4
|
-
|
|
5
|
-
COPY .mvn/ .mvn/
|
|
6
|
-
COPY mvnw pom.xml ./
|
|
7
|
-
RUN chmod +x mvnw && ./mvnw -q -DskipTests dependency:go-offline
|
|
8
|
-
|
|
9
|
-
COPY src ./src
|
|
4
|
+
COPY . .
|
|
10
5
|
RUN ./mvnw -q -DskipTests package
|
|
11
6
|
|
|
12
7
|
FROM eclipse-temurin:21-jre
|
|
@@ -2,24 +2,14 @@
|
|
|
2
2
|
package <%= group %>.<%= projectName %>.model;
|
|
3
3
|
|
|
4
4
|
import jakarta.persistence.*;
|
|
5
|
-
import lombok
|
|
5
|
+
import lombok.Data;
|
|
6
6
|
|
|
7
7
|
@Data
|
|
8
|
-
@NoArgsConstructor
|
|
9
|
-
@AllArgsConstructor
|
|
10
8
|
@Entity
|
|
11
|
-
@Table(name = "<%= modelName.toLowerCase() %>")
|
|
12
9
|
public class <%= modelName %> {
|
|
13
|
-
|
|
14
|
-
@Id
|
|
15
|
-
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
10
|
+
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
16
11
|
private Long id;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
: (f.type === 'int' || f.type === 'Integer') ? 'Integer'
|
|
21
|
-
: (f.type === 'boolean' || f.type === 'Boolean') ? 'Boolean'
|
|
22
|
-
: (f.type === 'date' || f.type === 'Date') ? 'java.time.Instant'
|
|
23
|
-
: 'String' %> <%= f.name %>;
|
|
24
|
-
<% }) -%>
|
|
12
|
+
<% model.fields.forEach(f => { %>
|
|
13
|
+
private <%= f.type === 'Number' ? 'Integer' : (f.type === 'Boolean' ? 'Boolean' : 'String') %> <%= f.name %>;
|
|
14
|
+
<% }) %>
|
|
25
15
|
}
|
|
@@ -5,9 +5,6 @@ import jakarta.servlet.FilterChain;
|
|
|
5
5
|
import jakarta.servlet.ServletException;
|
|
6
6
|
import jakarta.servlet.http.HttpServletRequest;
|
|
7
7
|
import jakarta.servlet.http.HttpServletResponse;
|
|
8
|
-
|
|
9
|
-
import io.jsonwebtoken.JwtException;
|
|
10
|
-
|
|
11
8
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
12
9
|
import org.springframework.security.core.userdetails.UserDetails;
|
|
13
10
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
@@ -28,39 +25,19 @@ public class JwtAuthFilter extends OncePerRequestFilter {
|
|
|
28
25
|
this.uds = uds;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
|
-
@Override
|
|
32
|
-
protected boolean shouldNotFilter(HttpServletRequest request) {
|
|
33
|
-
String path = request.getRequestURI();
|
|
34
|
-
return path != null && path.startsWith("/api/auth");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
28
|
@Override
|
|
38
29
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
|
39
30
|
throws ServletException, IOException {
|
|
40
|
-
|
|
41
|
-
// If already authenticated, skip
|
|
42
|
-
if (SecurityContextHolder.getContext().getAuthentication() != null) {
|
|
43
|
-
chain.doFilter(request, response);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
31
|
String header = request.getHeader("Authorization");
|
|
48
|
-
|
|
49
32
|
if (header != null && header.startsWith("Bearer ")) {
|
|
50
|
-
String token = header.substring(7)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
SecurityContextHolder.getContext().setAuthentication(auth);
|
|
58
|
-
} catch (JwtException ex) {
|
|
59
|
-
// Invalid/expired token: continue without authentication
|
|
60
|
-
}
|
|
61
|
-
}
|
|
33
|
+
String token = header.substring(7);
|
|
34
|
+
try {
|
|
35
|
+
String subject = jwtService.validateAndGetSubject(token);
|
|
36
|
+
UserDetails ud = uds.loadUserByUsername(subject);
|
|
37
|
+
var auth = new UsernamePasswordAuthenticationToken(ud, null, ud.getAuthorities());
|
|
38
|
+
SecurityContextHolder.getContext().setAuthentication(auth);
|
|
39
|
+
} catch (Exception ignored) {}
|
|
62
40
|
}
|
|
63
|
-
|
|
64
41
|
chain.doFilter(request, response);
|
|
65
42
|
}
|
|
66
43
|
}
|
|
@@ -5,54 +5,26 @@ import io.jsonwebtoken.*;
|
|
|
5
5
|
import io.jsonwebtoken.security.Keys;
|
|
6
6
|
import org.springframework.stereotype.Service;
|
|
7
7
|
|
|
8
|
-
import java.nio.charset.StandardCharsets;
|
|
9
8
|
import java.security.Key;
|
|
10
9
|
import java.util.Date;
|
|
11
10
|
|
|
12
11
|
@Service
|
|
13
12
|
public class JwtService {
|
|
13
|
+
private final Key key = Keys.hmacShaKeyFor(System.getenv("JWT_SECRET") != null
|
|
14
|
+
? System.getenv("JWT_SECRET").getBytes()
|
|
15
|
+
: "change_this_dev_secret_change_this_dev_secret".getBytes());
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
private final long expirationMs;
|
|
17
|
-
|
|
18
|
-
public JwtService() {
|
|
19
|
-
String secret = System.getenv("JWT_SECRET");
|
|
20
|
-
if (secret == null || secret.trim().length() < 32) {
|
|
21
|
-
// Must be >= 32 chars for HS256 keys (jjwt enforces minimum)
|
|
22
|
-
secret = "change_this_dev_secret_change_this_dev_secret";
|
|
23
|
-
}
|
|
24
|
-
this.key = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
|
25
|
-
|
|
26
|
-
String exp = System.getenv("JWT_EXPIRES_MS");
|
|
27
|
-
long fallback = 1000L * 60 * 60 * 5; // 5 hours
|
|
28
|
-
this.expirationMs = (exp != null) ? parseLongSafe(exp, fallback) : fallback;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
public String generateToken(String subject) {
|
|
32
|
-
Date now = new Date();
|
|
33
|
-
Date exp = new Date(now.getTime() + expirationMs);
|
|
34
|
-
|
|
17
|
+
public String generateToken(String userId) {
|
|
35
18
|
return Jwts.builder()
|
|
36
|
-
.setSubject(
|
|
37
|
-
.setIssuedAt(
|
|
38
|
-
.setExpiration(
|
|
19
|
+
.setSubject(userId)
|
|
20
|
+
.setIssuedAt(new Date())
|
|
21
|
+
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 5))
|
|
39
22
|
.signWith(key, SignatureAlgorithm.HS256)
|
|
40
23
|
.compact();
|
|
41
24
|
}
|
|
42
25
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
public String validateAndGetSubject(String token) throws JwtException {
|
|
47
|
-
return Jwts.parserBuilder()
|
|
48
|
-
.setSigningKey(key)
|
|
49
|
-
.build()
|
|
50
|
-
.parseClaimsJws(token)
|
|
51
|
-
.getBody()
|
|
52
|
-
.getSubject();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private long parseLongSafe(String v, long fallback) {
|
|
56
|
-
try { return Long.parseLong(v); } catch (Exception e) { return fallback; }
|
|
26
|
+
public String validateAndGetSubject(String token) {
|
|
27
|
+
return Jwts.parserBuilder().setSigningKey(key).build()
|
|
28
|
+
.parseClaimsJws(token).getBody().getSubject();
|
|
57
29
|
}
|
|
58
30
|
}
|
|
@@ -3,16 +3,7 @@ package <%= group %>.<%= projectName %>.repository;
|
|
|
3
3
|
|
|
4
4
|
import org.springframework.data.jpa.repository.JpaRepository;
|
|
5
5
|
import org.springframework.stereotype.Repository;
|
|
6
|
-
|
|
7
6
|
import <%= group %>.<%= projectName %>.model.<%= modelName %>;
|
|
8
7
|
|
|
9
8
|
@Repository
|
|
10
|
-
public interface <%= modelName %>Repository extends JpaRepository<<%= modelName %>, Long> {
|
|
11
|
-
|
|
12
|
-
<%
|
|
13
|
-
// If generator passes `extraFinders: [{name:'Email', type:'String'}]`
|
|
14
|
-
(extraFinders || []).forEach(f => {
|
|
15
|
-
-%>
|
|
16
|
-
java.util.Optional<<%= modelName %>> findBy<%= f.name %>(<%= f.type %> <%= f.name.charAt(0).toLowerCase() + f.name.slice(1) %>);
|
|
17
|
-
<% }) -%>
|
|
18
|
-
}
|
|
9
|
+
public interface <%= modelName %>Repository extends JpaRepository<<%= modelName %>, Long> {}
|
|
@@ -2,65 +2,27 @@
|
|
|
2
2
|
package <%= group %>.<%= projectName %>.service;
|
|
3
3
|
|
|
4
4
|
import org.springframework.stereotype.Service;
|
|
5
|
-
|
|
6
5
|
import java.util.List;
|
|
7
6
|
import java.util.Optional;
|
|
8
|
-
|
|
9
7
|
import <%= group %>.<%= projectName %>.repository.<%= modelName %>Repository;
|
|
10
8
|
import <%= group %>.<%= projectName %>.model.<%= modelName %>;
|
|
11
9
|
|
|
12
10
|
@Service
|
|
13
11
|
public class <%= modelName %>Service {
|
|
14
|
-
|
|
15
12
|
private final <%= modelName %>Repository repo;
|
|
13
|
+
public <%= modelName %>Service(<%= modelName %>Repository repo) { this.repo = repo; }
|
|
16
14
|
|
|
17
|
-
public
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public List<<%= modelName %>> findAll() {
|
|
22
|
-
return repo.findAll();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public Optional<<%= modelName %>> findById(Long id) {
|
|
26
|
-
return repo.findById(id);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public <%= modelName %> create(<%= modelName %> m) {
|
|
30
|
-
// Ensure id is not forced by client
|
|
31
|
-
m.setId(null);
|
|
32
|
-
return repo.save(m);
|
|
33
|
-
}
|
|
34
|
-
|
|
15
|
+
public List<<%= modelName %>> findAll() { return repo.findAll(); }
|
|
16
|
+
public Optional<<%= modelName %>> findById(Long id) { return repo.findById(id); }
|
|
17
|
+
public <%= modelName %> create(<%= modelName %> m) { return repo.save(m); }
|
|
35
18
|
public Optional<<%= modelName %>> update(Long id, <%= modelName %> m) {
|
|
36
19
|
return repo.findById(id).map(existing -> {
|
|
37
|
-
<% model.fields.forEach(f => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// Update <%= f.name %>
|
|
41
|
-
existing.set<%= cap %>(m.get<%= cap %>());
|
|
42
|
-
<% }) -%>
|
|
43
|
-
return repo.save(existing);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Partial update: only set non-null values.
|
|
49
|
-
* Useful if your frontend sends partial payloads.
|
|
50
|
-
*/
|
|
51
|
-
public Optional<<%= modelName %>> patch(Long id, <%= modelName %> m) {
|
|
52
|
-
return repo.findById(id).map(existing -> {
|
|
53
|
-
<% model.fields.forEach(f => {
|
|
54
|
-
const cap = f.name.charAt(0).toUpperCase() + f.name.slice(1);
|
|
55
|
-
-%>
|
|
56
|
-
if (m.get<%= cap %>() != null) {
|
|
57
|
-
existing.set<%= cap %>(m.get<%= cap %>());
|
|
58
|
-
}
|
|
59
|
-
<% }) -%>
|
|
20
|
+
<% model.fields.forEach(f => { %>
|
|
21
|
+
existing.set<%= f.name.charAt(0).toUpperCase() + f.name.slice(1) %>(m.get<%= f.name.charAt(0).toUpperCase() + f.name.slice(1) %>());
|
|
22
|
+
<% }) %>
|
|
60
23
|
return repo.save(existing);
|
|
61
24
|
});
|
|
62
25
|
}
|
|
63
|
-
|
|
64
26
|
public boolean delete(Long id) {
|
|
65
27
|
if (!repo.existsById(id)) return false;
|
|
66
28
|
repo.deleteById(id);
|
|
@@ -1,33 +1,20 @@
|
|
|
1
1
|
// Auto-generated by create-backlist
|
|
2
2
|
package <%= group %>.<%= projectName %>.model;
|
|
3
3
|
|
|
4
|
-
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
5
4
|
import jakarta.persistence.*;
|
|
6
|
-
import lombok
|
|
5
|
+
import lombok.Data;
|
|
7
6
|
|
|
8
7
|
@Data
|
|
9
|
-
@NoArgsConstructor
|
|
10
|
-
@AllArgsConstructor
|
|
11
8
|
@Entity
|
|
12
|
-
@Table(
|
|
13
|
-
name = "users",
|
|
14
|
-
indexes = {
|
|
15
|
-
@Index(name = "idx_users_email", columnList = "email", unique = true)
|
|
16
|
-
}
|
|
17
|
-
)
|
|
9
|
+
@Table(name="users")
|
|
18
10
|
public class User {
|
|
19
|
-
|
|
20
|
-
@Id
|
|
21
|
-
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
11
|
+
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
22
12
|
private Long id;
|
|
23
13
|
|
|
24
|
-
@Column(nullable = false)
|
|
25
14
|
private String name;
|
|
26
15
|
|
|
27
|
-
@Column(
|
|
16
|
+
@Column(unique = true)
|
|
28
17
|
private String email;
|
|
29
18
|
|
|
30
|
-
@JsonIgnore
|
|
31
|
-
@Column(nullable = false)
|
|
32
19
|
private String password;
|
|
33
20
|
}
|
|
@@ -3,12 +3,12 @@ package <%= group %>.<%= projectName %>.security;
|
|
|
3
3
|
|
|
4
4
|
import <%= group %>.<%= projectName %>.model.User;
|
|
5
5
|
import <%= group %>.<%= projectName %>.repository.UserRepository;
|
|
6
|
-
|
|
7
6
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
8
7
|
import org.springframework.security.core.userdetails.UserDetails;
|
|
9
8
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
10
|
-
|
|
11
9
|
import org.springframework.stereotype.Service;
|
|
10
|
+
import org.springframework.security.core.userdetails.User.UserBuilder;
|
|
11
|
+
import org.springframework.security.core.userdetails.User.*;
|
|
12
12
|
|
|
13
13
|
@Service
|
|
14
14
|
public class UserDetailsServiceImpl implements UserDetailsService {
|
|
@@ -21,13 +21,7 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
|
|
21
21
|
|
|
22
22
|
@Override
|
|
23
23
|
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
|
|
24
|
-
User u = repo.findByEmail(email)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return org.springframework.security.core.userdetails.User
|
|
28
|
-
.withUsername(u.getEmail())
|
|
29
|
-
.password(u.getPassword())
|
|
30
|
-
.authorities("ROLE_USER")
|
|
31
|
-
.build();
|
|
24
|
+
User u = repo.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException("Not found"));
|
|
25
|
+
return withUsername(u.getEmail()).password(u.getPassword()).authorities("USER").build();
|
|
32
26
|
}
|
|
33
27
|
}
|
|
@@ -2,19 +2,11 @@
|
|
|
2
2
|
package <%= group %>.<%= projectName %>.repository;
|
|
3
3
|
|
|
4
4
|
import java.util.Optional;
|
|
5
|
-
|
|
6
5
|
import org.springframework.data.jpa.repository.JpaRepository;
|
|
7
6
|
import org.springframework.stereotype.Repository;
|
|
8
|
-
|
|
9
7
|
import <%= group %>.<%= projectName %>.model.User;
|
|
10
8
|
|
|
11
9
|
@Repository
|
|
12
10
|
public interface UserRepository extends JpaRepository<User, Long> {
|
|
13
|
-
|
|
14
11
|
Optional<User> findByEmail(String email);
|
|
15
|
-
|
|
16
|
-
boolean existsByEmail(String email);
|
|
17
|
-
|
|
18
|
-
// Optional: helpful if emails are stored normalized but requests vary
|
|
19
|
-
Optional<User> findByEmailIgnoreCase(String email);
|
|
20
12
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
version:
|
|
2
|
-
|
|
1
|
+
version: '3.8'
|
|
3
2
|
services:
|
|
4
3
|
db:
|
|
5
4
|
image: postgres:16-alpine
|
|
@@ -8,28 +7,21 @@ services:
|
|
|
8
7
|
POSTGRES_PASSWORD: ${DB_PASSWORD:-password}
|
|
9
8
|
POSTGRES_DB: ${DB_NAME:-<%= projectName %>}
|
|
10
9
|
ports:
|
|
11
|
-
- "
|
|
10
|
+
- "5432:5432"
|
|
12
11
|
volumes:
|
|
13
12
|
- pgdata:/var/lib/postgresql/data
|
|
14
|
-
healthcheck:
|
|
15
|
-
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-<%= projectName %>}"]
|
|
16
|
-
interval: 5s
|
|
17
|
-
timeout: 5s
|
|
18
|
-
retries: 20
|
|
19
13
|
|
|
20
14
|
app:
|
|
21
15
|
build: .
|
|
22
16
|
depends_on:
|
|
23
|
-
db
|
|
24
|
-
condition: service_healthy
|
|
17
|
+
- db
|
|
25
18
|
environment:
|
|
26
|
-
JWT_SECRET
|
|
27
|
-
SPRING_DATASOURCE_URL
|
|
28
|
-
SPRING_DATASOURCE_USERNAME
|
|
29
|
-
SPRING_DATASOURCE_PASSWORD
|
|
30
|
-
SPRING_JPA_HIBERNATE_DDL_AUTO: ${JPA_DDL_AUTO:-update}
|
|
19
|
+
- JWT_SECRET=${JWT_SECRET:-change_me_long_secret}
|
|
20
|
+
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/${DB_NAME:-<%= projectName %>}
|
|
21
|
+
- SPRING_DATASOURCE_USERNAME=${DB_USER:-postgres}
|
|
22
|
+
- SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD:-password}
|
|
31
23
|
ports:
|
|
32
|
-
- "
|
|
24
|
+
- "8080:8080"
|
|
33
25
|
|
|
34
26
|
volumes:
|
|
35
27
|
pgdata:
|
|
@@ -4,26 +4,19 @@ import dotenv from 'dotenv';
|
|
|
4
4
|
|
|
5
5
|
dotenv.config();
|
|
6
6
|
|
|
7
|
-
const app: Express = express();
|
|
7
|
+
const app: Express = express(); // Added :Express type
|
|
8
8
|
|
|
9
9
|
app.use(cors());
|
|
10
10
|
app.use(express.json());
|
|
11
11
|
|
|
12
|
-
app.get('/', (req: Request, res: Response) => {
|
|
12
|
+
app.get('/', (req: Request, res: Response) => { // Added :Request and :Response types
|
|
13
13
|
res.send('Node.js Backend says Hello! Generated by Backlist.');
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
//
|
|
17
|
-
// </backlist:imports>
|
|
16
|
+
// INJECT:ROUTES
|
|
18
17
|
|
|
19
|
-
//
|
|
20
|
-
// </backlist:setup>
|
|
21
|
-
|
|
22
|
-
// <backlist:routes>
|
|
23
|
-
// </backlist:routes>
|
|
24
|
-
|
|
25
|
-
const PORT: number | string = process.env.PORT || 8000;
|
|
18
|
+
const PORT: number | string = process.env.PORT || 8000; // Added type for PORT
|
|
26
19
|
|
|
27
20
|
app.listen(PORT, () => {
|
|
28
|
-
console.log(
|
|
29
|
-
});
|
|
21
|
+
console.log(`⚡️ Server running on http://localhost:${PORT}`);
|
|
22
|
+
});
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
3
|
+
"target": "es6",
|
|
4
4
|
"module": "commonjs",
|
|
5
|
-
"moduleResolution": "node",
|
|
6
5
|
"rootDir": "./src",
|
|
7
6
|
"outDir": "./dist",
|
|
8
|
-
|
|
9
7
|
"esModuleInterop": true,
|
|
10
|
-
"strict": true
|
|
11
|
-
|
|
12
|
-
"types": ["node"],
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"forceConsistentCasingInFileNames": true,
|
|
15
|
-
"skipLibCheck": true,
|
|
16
|
-
|
|
17
|
-
"sourceMap": true
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*"]
|
|
8
|
+
"strict": true
|
|
9
|
+
}
|
|
20
10
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Auto-generated by create-backlist v5.
|
|
1
|
+
// Auto-generated by create-backlist v5.0
|
|
2
2
|
import swaggerUi from 'swagger-ui-express';
|
|
3
3
|
import swaggerJsdoc from 'swagger-jsdoc';
|
|
4
4
|
import { Express } from 'express';
|
|
@@ -17,27 +17,17 @@ const options: swaggerJsdoc.Options = {
|
|
|
17
17
|
description: 'Development server',
|
|
18
18
|
},
|
|
19
19
|
],
|
|
20
|
-
|
|
21
|
-
components: {
|
|
22
|
-
securitySchemes: {
|
|
23
|
-
bearerAuth: {
|
|
24
|
-
type: 'http',
|
|
25
|
-
scheme: 'bearer',
|
|
26
|
-
bearerFormat: 'JWT',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
security: [{ bearerAuth: [] }],
|
|
31
|
-
<% } -%>
|
|
20
|
+
// TODO: Add components (e.g., securitySchemes for JWT)
|
|
32
21
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
apis: ['./src/**/*.ts'],
|
|
22
|
+
// Path to the API docs
|
|
23
|
+
apis: ['./src/routes/*.ts', './src/routes.ts'], // Looks for JSDoc comments in routes
|
|
36
24
|
};
|
|
37
25
|
|
|
38
26
|
const swaggerSpec = swaggerJsdoc(options);
|
|
39
27
|
|
|
40
28
|
export function setupSwagger(app: Express) {
|
|
41
29
|
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
|
|
42
|
-
console.log(
|
|
30
|
+
console.log(
|
|
31
|
+
`📄 API documentation is available at http://localhost:<%= port %>/api-docs`
|
|
32
|
+
);
|
|
43
33
|
}
|
|
@@ -1,61 +1,38 @@
|
|
|
1
|
-
// Auto-generated by create-backlist v5.
|
|
1
|
+
// Auto-generated by create-backlist v5.0
|
|
2
2
|
import request from 'supertest';
|
|
3
3
|
import express from 'express';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import authRoutes from '../routes/Auth.routes';
|
|
9
|
-
<% } -%>
|
|
4
|
+
// Import your main app configuration. This might need path adjustment.
|
|
5
|
+
// For simplicity, we create a test server here.
|
|
6
|
+
import apiRoutes from '../routes'; // Assuming main routes
|
|
7
|
+
import authRoutes from '../routes/Auth.routes'; // Assuming auth routes
|
|
10
8
|
|
|
11
9
|
const app = express();
|
|
12
10
|
app.use(express.json());
|
|
13
|
-
|
|
14
|
-
<% if (addAuth) { -%>
|
|
11
|
+
app.use('/api', apiRoutes);
|
|
15
12
|
app.use('/api/auth', authRoutes);
|
|
16
|
-
<% } -%>
|
|
17
13
|
|
|
18
|
-
app.use('/api', apiRoutes);
|
|
19
14
|
|
|
20
|
-
describe('API Endpoints
|
|
21
|
-
|
|
15
|
+
describe('API Endpoints', () => {
|
|
16
|
+
|
|
17
|
+
it('should respond to the root GET endpoint', async () => {
|
|
18
|
+
// This test assumes a GET /api/ endpoint exists or a similar public one.
|
|
19
|
+
// You might need to adjust this to a real endpoint from your app.
|
|
20
|
+
// Example for GET /api/users
|
|
21
|
+
// const res = await request(app).get('/api/users');
|
|
22
|
+
// expect(res.statusCode).toEqual(200);
|
|
23
|
+
|
|
24
|
+
// For now, a placeholder test:
|
|
22
25
|
expect(1 + 1).toBe(2);
|
|
23
26
|
});
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// replace ":id" style params with dummy value
|
|
36
|
-
.replace(/:\w+/g, '1');
|
|
37
|
-
-%>
|
|
38
|
-
it('<%= ep.method %> <%= url %> should respond', async () => {
|
|
39
|
-
const req = request(app).<%= method %>('<%= '/api' + url %>');
|
|
40
|
-
|
|
41
|
-
<% if (['post','put','patch'].includes(method) && ep.schemaFields) { -%>
|
|
42
|
-
req.send(
|
|
43
|
-
<%- JSON.stringify(
|
|
44
|
-
Object.fromEntries(
|
|
45
|
-
Object.entries(ep.schemaFields).map(([k, t]) => [
|
|
46
|
-
k,
|
|
47
|
-
t === 'Number' ? 1 : (t === 'Boolean' ? true : 'test')
|
|
48
|
-
])
|
|
49
|
-
)
|
|
50
|
-
) %>
|
|
51
|
-
);
|
|
52
|
-
<% } -%>
|
|
53
|
-
|
|
54
|
-
const res = await req;
|
|
55
|
-
|
|
56
|
-
// We only assert "not 404" because generated handlers may be TODO stubs,
|
|
57
|
-
// and auth/validation may affect exact status codes.
|
|
58
|
-
expect(res.statusCode).not.toBe(404);
|
|
59
|
-
});
|
|
60
|
-
<% }) -%>
|
|
28
|
+
// TODO: Add more specific tests for your generated endpoints
|
|
29
|
+
// describe('POST /api/users', () => {
|
|
30
|
+
// it('should create a new user', async () => {
|
|
31
|
+
// const res = await request(app)
|
|
32
|
+
// .post('/api/users')
|
|
33
|
+
// .send({ name: 'Test User', email: 'test@example.com', password: 'password123' });
|
|
34
|
+
// expect(res.statusCode).toEqual(201);
|
|
35
|
+
// expect(res.body).toHaveProperty('name', 'Test User');
|
|
36
|
+
// });
|
|
37
|
+
// });
|
|
61
38
|
});
|