kybernus 2.0.10 → 2.1.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.
- package/node_modules/{@isaacs/balanced-match → balanced-match}/README.md +7 -10
- package/node_modules/{@isaacs/balanced-match → balanced-match}/package.json +5 -16
- package/node_modules/{@isaacs/brace-expansion → brace-expansion}/README.md +2 -5
- package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.js +1 -1
- package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.js.map +1 -1
- package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.js +1 -1
- package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.js.map +1 -1
- package/node_modules/{@isaacs/brace-expansion → brace-expansion}/package.json +9 -5
- package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
- package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
- package/node_modules/glob/dist/commonjs/glob.js +2 -1
- package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
- package/node_modules/glob/dist/commonjs/index.min.js +4 -0
- package/node_modules/glob/dist/commonjs/index.min.js.map +7 -0
- package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
- package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
- package/node_modules/glob/dist/commonjs/pattern.js +4 -0
- package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
- package/node_modules/glob/dist/esm/glob.d.ts +8 -0
- package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
- package/node_modules/glob/dist/esm/glob.js +2 -1
- package/node_modules/glob/dist/esm/glob.js.map +1 -1
- package/node_modules/glob/dist/esm/index.min.js +4 -0
- package/node_modules/glob/dist/esm/index.min.js.map +7 -0
- package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
- package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
- package/node_modules/glob/dist/esm/pattern.js +4 -0
- package/node_modules/glob/dist/esm/pattern.js.map +1 -1
- package/node_modules/glob/package.json +30 -10
- package/node_modules/lru-cache/package.json +7 -7
- package/node_modules/minimatch/README.md +3 -1
- package/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
- package/node_modules/minimatch/dist/commonjs/ast.js +37 -27
- package/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
- package/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
- package/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
- package/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
- package/node_modules/minimatch/dist/commonjs/escape.js +4 -4
- package/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
- package/node_modules/minimatch/dist/commonjs/index.d.ts +50 -0
- package/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/minimatch/dist/commonjs/index.js +37 -31
- package/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
- package/node_modules/minimatch/dist/commonjs/unescape.js +4 -4
- package/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
- package/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
- package/node_modules/minimatch/dist/esm/ast.js +37 -27
- package/node_modules/minimatch/dist/esm/ast.js.map +1 -1
- package/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
- package/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
- package/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
- package/node_modules/minimatch/dist/esm/escape.js +4 -4
- package/node_modules/minimatch/dist/esm/escape.js.map +1 -1
- package/node_modules/minimatch/dist/esm/index.d.ts +50 -0
- package/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
- package/node_modules/minimatch/dist/esm/index.js +37 -31
- package/node_modules/minimatch/dist/esm/index.js.map +1 -1
- package/node_modules/minimatch/dist/esm/unescape.js +4 -4
- package/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
- package/node_modules/minimatch/package.json +3 -3
- package/node_modules/minipass/LICENSE.md +55 -0
- package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
- package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/minipass/dist/commonjs/index.js +13 -3
- package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
- package/node_modules/minipass/dist/esm/index.d.ts +12 -16
- package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
- package/node_modules/minipass/dist/esm/index.js +3 -1
- package/node_modules/minipass/dist/esm/index.js.map +1 -1
- package/node_modules/minipass/package.json +9 -14
- package/node_modules/rimraf/README.md +29 -0
- package/node_modules/rimraf/dist/commonjs/fs.d.ts +9 -8
- package/node_modules/rimraf/dist/commonjs/fs.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/index.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/index.js +9 -3
- package/node_modules/rimraf/dist/commonjs/index.js.map +1 -1
- package/node_modules/rimraf/dist/commonjs/opt-arg.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/opt-arg.js +2 -1
- package/node_modules/rimraf/dist/commonjs/opt-arg.js.map +1 -1
- package/node_modules/rimraf/dist/commonjs/path-arg.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/path-arg.js +2 -1
- package/node_modules/rimraf/dist/commonjs/path-arg.js.map +1 -1
- package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts +2 -2
- package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.js.map +1 -1
- package/node_modules/rimraf/dist/commonjs/rimraf-posix.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/rimraf-posix.js +1 -2
- package/node_modules/rimraf/dist/commonjs/rimraf-posix.js.map +1 -1
- package/node_modules/rimraf/dist/commonjs/rimraf-windows.d.ts.map +1 -1
- package/node_modules/rimraf/dist/commonjs/rimraf-windows.js.map +1 -1
- package/node_modules/rimraf/dist/esm/bin.mjs.map +1 -1
- package/node_modules/rimraf/dist/esm/fs.d.ts +9 -8
- package/node_modules/rimraf/dist/esm/fs.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/index.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/index.js +10 -4
- package/node_modules/rimraf/dist/esm/index.js.map +1 -1
- package/node_modules/rimraf/dist/esm/opt-arg.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/opt-arg.js +2 -1
- package/node_modules/rimraf/dist/esm/opt-arg.js.map +1 -1
- package/node_modules/rimraf/dist/esm/path-arg.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/path-arg.js +2 -1
- package/node_modules/rimraf/dist/esm/path-arg.js.map +1 -1
- package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts +2 -2
- package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-move-remove.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-move-remove.js +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-move-remove.js.map +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-posix.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-posix.js +1 -2
- package/node_modules/rimraf/dist/esm/rimraf-posix.js.map +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-windows.d.ts.map +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-windows.js +1 -1
- package/node_modules/rimraf/dist/esm/rimraf-windows.js.map +1 -1
- package/node_modules/rimraf/package.json +4 -19
- package/package.json +1 -1
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/PostgresUserRepository.java.hbs +40 -0
- package/templates/java-spring/clean/src/main/resources/application.properties.hbs +18 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/web/controller → adapters/inbound/web}/AuthController.java.hbs +4 -5
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/JpaUserAdapter.java.hbs +40 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/entity/UserEntity.java.hbs +61 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/persistence/repository/JpaUserRepository.java.hbs +11 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{infrastructure/security/SecurityAdapters.java.hbs → adapters/outbound/security/SecurityAdapter.java.hbs} +14 -14
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/entity → core/domain}/User.java.hbs +2 -2
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/LoginUserUseCase.java.hbs +8 -8
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/usecase → core/ports/inbound}/RegisterUserUseCase.java.hbs +7 -8
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{domain/repository → core/ports/outbound}/UserRepository.java.hbs +4 -4
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{application → core}/service/AuthService.java.hbs +9 -9
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/{{projectNamePascalCase}}Application.java.hbs +2 -2
- package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +18 -0
- package/templates/nestjs/clean/package.json.hbs +9 -3
- package/templates/nestjs/clean/prisma/schema.prisma.hbs +20 -0
- package/templates/nestjs/clean/src/app.module.ts.hbs +17 -0
- package/templates/nestjs/clean/src/auth.module.ts.hbs +12 -10
- package/templates/nestjs/clean/src/infrastructure/database/prisma.service.ts.hbs +13 -0
- package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +32 -0
- package/templates/nestjs/clean/src/main.ts.hbs +11 -0
- package/templates/nestjs/hexagonal/package.json.hbs +9 -3
- package/templates/nestjs/hexagonal/prisma/schema.prisma +20 -0
- package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.service.ts.hbs +13 -0
- package/templates/nestjs/hexagonal/src/adapters/outbound/persistence/prisma.user.adapter.ts.hbs +32 -0
- package/templates/nestjs/hexagonal/src/app.module.ts.hbs +17 -0
- package/templates/nestjs/hexagonal/src/auth.module.ts.hbs +15 -13
- package/templates/nestjs/hexagonal/src/main.ts.hbs +11 -0
- package/templates/nextjs/mvc/package.json.hbs +35 -32
- package/templates/nextjs/mvc/prisma/schema.prisma.hbs +12 -9
- package/templates/nextjs/mvc/src/lib/db.ts +15 -0
- package/templates/nodejs-express/clean/docker-compose.yml.hbs +5 -6
- package/templates/nodejs-express/clean/package.json.hbs +14 -8
- package/templates/nodejs-express/clean/prisma/schema.prisma +20 -0
- package/templates/nodejs-express/clean/src/config/index.ts +27 -0
- package/templates/nodejs-express/clean/src/index.ts.hbs +20 -24
- package/templates/nodejs-express/clean/src/infrastructure/database/PrismaUserRepository.ts.hbs +61 -0
- package/templates/nodejs-express/clean/src/infrastructure/database/prisma.ts.hbs +5 -0
- package/templates/nodejs-express/clean/src/infrastructure/http/controllers/AuthController.ts.hbs +24 -40
- package/templates/nodejs-express/clean/src/infrastructure/http/middlewares/errorHandler.ts +24 -0
- package/templates/nodejs-express/clean/tsconfig.json.hbs +8 -17
- package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +5 -6
- package/templates/nodejs-express/hexagonal/package.json.hbs +14 -8
- package/templates/nodejs-express/hexagonal/prisma/schema.prisma +20 -0
- package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/AuthController.ts.hbs +29 -44
- package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/middlewares/errorHandler.ts +24 -0
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/PrismaUserAdapter.ts.hbs +61 -0
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +5 -0
- package/templates/nodejs-express/hexagonal/src/config/index.ts +27 -0
- package/templates/nodejs-express/hexagonal/src/index.ts.hbs +24 -27
- package/templates/nodejs-express/hexagonal/tsconfig.json.hbs +8 -17
- package/templates/python-fastapi/clean/app/application/services/__init__.py +0 -0
- package/templates/python-fastapi/clean/app/application/services/user_service.py.hbs +20 -0
- package/templates/python-fastapi/clean/app/config.py.hbs +24 -0
- package/templates/python-fastapi/clean/app/infrastructure/database/models.py.hbs +24 -0
- package/templates/python-fastapi/clean/app/infrastructure/database/postgres_repository.py.hbs +62 -0
- package/templates/python-fastapi/clean/app/infrastructure/database/session.py.hbs +27 -0
- package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +14 -8
- package/templates/python-fastapi/clean/app/main.py.hbs +25 -3
- package/templates/python-fastapi/clean/requirements.txt.hbs +3 -1
- package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +41 -17
- package/templates/python-fastapi/hexagonal/app/adapters/outbound/postgres_user_repository.py.hbs +50 -0
- package/templates/python-fastapi/hexagonal/app/config.py.hbs +20 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/models.py.hbs +24 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +20 -0
- package/templates/python-fastapi/hexagonal/app/main.py.hbs +22 -14
- package/templates/python-fastapi/hexagonal/requirements.txt.hbs +3 -1
- package/node_modules/minipass/LICENSE +0 -15
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/infrastructure/persistence/InMemoryUserRepository.java.hbs +0 -41
- package/templates/nestjs/clean/src/infrastructure/database/in-memory.repository.ts.hbs +0 -17
- package/templates/nodejs-express/clean/src/infrastructure/database/InMemoryUserRepository.ts.hbs +0 -46
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/InMemoryUserAdapter.ts.hbs +0 -38
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/LICENSE.md +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.d.ts +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.js +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/index.js.map +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/commonjs/package.json +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.d.ts +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.js +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/index.js.map +0 -0
- /package/node_modules/{@isaacs/balanced-match → balanced-match}/dist/esm/package.json +0 -0
- /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/LICENSE +0 -0
- /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.d.ts +0 -0
- /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/commonjs/package.json +0 -0
- /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.d.ts +0 -0
- /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/index.d.ts.map +0 -0
- /package/node_modules/{@isaacs/brace-expansion → brace-expansion}/dist/esm/package.json +0 -0
- /package/templates/python-fastapi/hexagonal/app/core/{ports.py.hbs → ports/ports.py.hbs} +0 -0
|
@@ -1,41 +1,38 @@
|
|
|
1
|
+
import 'express-async-errors';
|
|
1
2
|
import express from 'express';
|
|
2
3
|
import cors from 'cors';
|
|
3
4
|
import helmet from 'helmet';
|
|
4
5
|
import morgan from 'morgan';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
// Adapters
|
|
11
|
-
import { InMemoryUserAdapter } from './adapters/outbound/persistence/InMemoryUserAdapter';
|
|
12
|
-
import { bcryptAdapter, jwtAdapter } from './adapters/outbound/SecurityAdapters';
|
|
13
|
-
import { createAuthController } from './adapters/inbound/http/AuthController';
|
|
14
|
-
|
|
15
|
-
dotenv.config();
|
|
6
|
+
import { config } from './config';
|
|
7
|
+
import { PrismaUserAdapter } from './adapters/outbound/persistence/PrismaUserAdapter';
|
|
8
|
+
import { AuthService } from './core/services/AuthService';
|
|
9
|
+
import { AuthController } from './adapters/inbound/http/AuthController';
|
|
10
|
+
import { errorHandler } from './adapters/inbound/http/middlewares/errorHandler';
|
|
16
11
|
|
|
17
12
|
const app = express();
|
|
18
13
|
|
|
19
|
-
//
|
|
20
|
-
app.use(
|
|
14
|
+
// Middlewares
|
|
15
|
+
app.use(express.json());
|
|
21
16
|
app.use(cors());
|
|
17
|
+
app.use(helmet());
|
|
22
18
|
app.use(morgan('dev'));
|
|
23
|
-
app.use(express.json());
|
|
24
19
|
|
|
25
|
-
// Dependency Injection
|
|
26
|
-
const userRepository = new
|
|
27
|
-
const authService = new AuthService(userRepository
|
|
20
|
+
// Dependency Injection
|
|
21
|
+
const userRepository = new PrismaUserAdapter();
|
|
22
|
+
const authService = new AuthService(userRepository);
|
|
23
|
+
const authController = new AuthController(authService);
|
|
28
24
|
|
|
29
|
-
//
|
|
30
|
-
app.
|
|
25
|
+
// Routes
|
|
26
|
+
app.post('/api/auth/register', (req, res, next) => authController.register(req, res, next));
|
|
27
|
+
app.post('/api/auth/login', (req, res, next) => authController.login(req, res, next));
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
app.get('/health', (req, res) => {
|
|
30
|
+
res.json({ status: 'ok', architecture: 'hexagonal' });
|
|
31
|
+
});
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
// Error Handler
|
|
34
|
+
app.use(errorHandler);
|
|
36
35
|
|
|
37
|
-
app.listen(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
console.log(`🔗 Health check: http://localhost:${PORT}/health`);
|
|
41
|
-
});
|
|
36
|
+
app.listen(config.port, () => {
|
|
37
|
+
console.log(`🚀 Server running on port ${config.port}`);
|
|
38
|
+
});
|
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
3
|
+
"target": "es2020",
|
|
4
4
|
"module": "commonjs",
|
|
5
|
-
"lib": [
|
|
6
|
-
"ES2022"
|
|
7
|
-
],
|
|
5
|
+
"lib": ["es2020"],
|
|
8
6
|
"outDir": "./dist",
|
|
9
7
|
"rootDir": "./src",
|
|
10
8
|
"strict": true,
|
|
11
9
|
"esModuleInterop": true,
|
|
12
10
|
"skipLibCheck": true,
|
|
13
11
|
"forceConsistentCasingInFileNames": true,
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"node"
|
|
18
|
-
]
|
|
12
|
+
"experimentalDecorators": true,
|
|
13
|
+
"emitDecoratorMetadata": true,
|
|
14
|
+
"resolveJsonModule": true
|
|
19
15
|
},
|
|
20
|
-
"include": [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"exclude": [
|
|
24
|
-
"node_modules",
|
|
25
|
-
"dist"
|
|
26
|
-
]
|
|
27
|
-
}
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"]
|
|
18
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from app.domain.entities.user import User
|
|
3
|
+
from app.domain.repositories.user_repository import IUserRepository
|
|
4
|
+
from app.domain.usecases.register_user import RegisterUserUseCase, IPasswordHasher, ITokenGenerator
|
|
5
|
+
|
|
6
|
+
# In Clean Architecture, Services usually orchestrate Use Cases or coordinate cross-domain logic.
|
|
7
|
+
# For simple CRUD, Use Cases might be sufficient, but here is a Service example.
|
|
8
|
+
|
|
9
|
+
class UserService:
|
|
10
|
+
def __init__(self, repository: IUserRepository):
|
|
11
|
+
self.repository = repository
|
|
12
|
+
|
|
13
|
+
async def get_user(self, user_id: str) -> Optional[User]:
|
|
14
|
+
return await self.repository.find_by_id(user_id)
|
|
15
|
+
|
|
16
|
+
async def get_user_by_email(self, email: str) -> Optional[User]:
|
|
17
|
+
return await self.repository.find_by_email(email)
|
|
18
|
+
|
|
19
|
+
# Note: Registration logic is handled by RegisterUserUseCase, aligning with Clean Architecture
|
|
20
|
+
# where specific complex actions are Use Cases.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
|
|
4
|
+
class Settings(BaseSettings):
|
|
5
|
+
PROJECT_NAME: str = "{{projectName}}"
|
|
6
|
+
API_V1_STR: str = "/api/v1"
|
|
7
|
+
|
|
8
|
+
# Database
|
|
9
|
+
# Use asyncpg for async SQLAlchemy
|
|
10
|
+
DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/{{projectName}}_db"
|
|
11
|
+
|
|
12
|
+
# Security
|
|
13
|
+
SECRET_KEY: str = "change_this_to_a_secure_random_key"
|
|
14
|
+
ALGORITHM: str = "HS256"
|
|
15
|
+
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
|
16
|
+
|
|
17
|
+
# Stripe
|
|
18
|
+
STRIPE_API_KEY: str | None = None
|
|
19
|
+
|
|
20
|
+
model_config = SettingsConfigDict(env_file=".env", case_sensitive=True)
|
|
21
|
+
|
|
22
|
+
@lru_cache
|
|
23
|
+
def get_settings():
|
|
24
|
+
return Settings()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, DateTime, Boolean
|
|
2
|
+
from sqlalchemy.sql import func
|
|
3
|
+
from app.infrastructure.database.session import Base
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
class UserModel(Base):
|
|
7
|
+
__tablename__ = "users"
|
|
8
|
+
|
|
9
|
+
id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4()))
|
|
10
|
+
email = Column(String, unique=True, index=True, nullable=False)
|
|
11
|
+
name = Column(String, nullable=False)
|
|
12
|
+
password = Column(String, nullable=False)
|
|
13
|
+
stripe_customer_id = Column(String, nullable=True)
|
|
14
|
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
15
|
+
is_active = Column(Boolean, default=True)
|
|
16
|
+
|
|
17
|
+
def to_dict(self):
|
|
18
|
+
return {
|
|
19
|
+
"id": self.id,
|
|
20
|
+
"email": self.email,
|
|
21
|
+
"name": self.name,
|
|
22
|
+
"stripe_customer_id": self.stripe_customer_id,
|
|
23
|
+
"created_at": self.created_at
|
|
24
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
|
+
from sqlalchemy import select, delete
|
|
4
|
+
from app.domain.repositories.user_repository import IUserRepository
|
|
5
|
+
from app.domain.entities.user import User
|
|
6
|
+
from app.infrastructure.database.models import UserModel
|
|
7
|
+
|
|
8
|
+
class PostgresUserRepository(IUserRepository):
|
|
9
|
+
def __init__(self, session: AsyncSession):
|
|
10
|
+
self.session = session
|
|
11
|
+
|
|
12
|
+
def _to_entity(self, model: UserModel) -> User:
|
|
13
|
+
return User(
|
|
14
|
+
id=model.id,
|
|
15
|
+
email=model.email,
|
|
16
|
+
name=model.name,
|
|
17
|
+
password=model.password,
|
|
18
|
+
stripe_customer_id=model.stripe_customer_id,
|
|
19
|
+
created_at=model.created_at
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
async def find_by_id(self, user_id: str) -> Optional[User]:
|
|
23
|
+
result = await self.session.execute(select(UserModel).where(UserModel.id == user_id))
|
|
24
|
+
model = result.scalars().first()
|
|
25
|
+
return self._to_entity(model) if model else None
|
|
26
|
+
|
|
27
|
+
async def find_by_email(self, email: str) -> Optional[User]:
|
|
28
|
+
result = await self.session.execute(select(UserModel).where(UserModel.email == email))
|
|
29
|
+
model = result.scalars().first()
|
|
30
|
+
return self._to_entity(model) if model else None
|
|
31
|
+
|
|
32
|
+
async def save(self, user: User) -> User:
|
|
33
|
+
# Check if exists to decide update vs insert (simplistic approach)
|
|
34
|
+
# In a real app, optimize this or use merge
|
|
35
|
+
existing = await self.find_by_id(user.id)
|
|
36
|
+
|
|
37
|
+
if existing:
|
|
38
|
+
stmt = select(UserModel).where(UserModel.id == user.id)
|
|
39
|
+
result = await self.session.execute(stmt)
|
|
40
|
+
model = result.scalars().first()
|
|
41
|
+
model.email = user.email
|
|
42
|
+
model.name = user.name
|
|
43
|
+
model.password = user.password
|
|
44
|
+
model.stripe_customer_id = user.stripe_customer_id
|
|
45
|
+
else:
|
|
46
|
+
model = UserModel(
|
|
47
|
+
id=user.id,
|
|
48
|
+
email=user.email,
|
|
49
|
+
name=user.name,
|
|
50
|
+
password=user.password,
|
|
51
|
+
stripe_customer_id=user.stripe_customer_id,
|
|
52
|
+
created_at=user.created_at
|
|
53
|
+
)
|
|
54
|
+
self.session.add(model)
|
|
55
|
+
|
|
56
|
+
await self.session.commit()
|
|
57
|
+
await self.session.refresh(model)
|
|
58
|
+
return self._to_entity(model)
|
|
59
|
+
|
|
60
|
+
async def delete(self, user_id: str) -> None:
|
|
61
|
+
await self.session.execute(delete(UserModel).where(UserModel.id == user_id))
|
|
62
|
+
await self.session.commit()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
|
2
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
3
|
+
from app.config import get_settings
|
|
4
|
+
|
|
5
|
+
settings = get_settings()
|
|
6
|
+
|
|
7
|
+
engine = create_async_engine(
|
|
8
|
+
settings.DATABASE_URL,
|
|
9
|
+
echo=True, # Set to False in production
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
AsyncSessionLocal = async_sessionmaker(
|
|
13
|
+
bind=engine,
|
|
14
|
+
class_=AsyncSession,
|
|
15
|
+
expire_on_commit=False,
|
|
16
|
+
autoflush=False,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
class Base(DeclarativeBase):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
async def get_db():
|
|
23
|
+
async with AsyncSessionLocal() as session:
|
|
24
|
+
try:
|
|
25
|
+
yield session
|
|
26
|
+
finally:
|
|
27
|
+
await session.close()
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
from fastapi import APIRouter, HTTPException, Depends
|
|
2
2
|
from pydantic import BaseModel, EmailStr
|
|
3
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
4
|
from app.domain.usecases.register_user import RegisterUserUseCase
|
|
4
|
-
from app.infrastructure.database.
|
|
5
|
+
from app.infrastructure.database.postgres_repository import PostgresUserRepository
|
|
5
6
|
from app.infrastructure.security.adapters import BcryptHasher, JwtTokenGenerator
|
|
7
|
+
from app.infrastructure.database.session import get_db
|
|
6
8
|
|
|
7
9
|
router = APIRouter()
|
|
8
10
|
|
|
9
|
-
# Dependency Injection
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
# Dependency Injection Factory
|
|
12
|
+
def get_register_usecase(db: AsyncSession = Depends(get_db)) -> RegisterUserUseCase:
|
|
13
|
+
repo = PostgresUserRepository(db)
|
|
14
|
+
hasher = BcryptHasher()
|
|
15
|
+
token_gen = JwtTokenGenerator()
|
|
16
|
+
return RegisterUserUseCase(repo, hasher, token_gen)
|
|
14
17
|
|
|
15
18
|
class RegisterRequest(BaseModel):
|
|
16
19
|
email: EmailStr
|
|
@@ -18,9 +21,12 @@ class RegisterRequest(BaseModel):
|
|
|
18
21
|
password: str
|
|
19
22
|
|
|
20
23
|
@router.post("/register")
|
|
21
|
-
async def register(
|
|
24
|
+
async def register(
|
|
25
|
+
req: RegisterRequest,
|
|
26
|
+
usecase: RegisterUserUseCase = Depends(get_register_usecase)
|
|
27
|
+
):
|
|
22
28
|
try:
|
|
23
|
-
result = await
|
|
29
|
+
result = await usecase.execute(req.email, req.name, req.password)
|
|
24
30
|
return result
|
|
25
31
|
except ValueError as e:
|
|
26
32
|
raise HTTPException(status_code=400, detail=str(e))
|
|
@@ -1,10 +1,32 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
1
2
|
from fastapi import FastAPI
|
|
2
3
|
from app.infrastructure.http import auth_controller
|
|
4
|
+
from app.config import get_settings
|
|
5
|
+
from app.infrastructure.database.session import engine, Base
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
settings = get_settings()
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
@asynccontextmanager
|
|
10
|
+
async def lifespan(app: FastAPI):
|
|
11
|
+
# Create tables on startup (for development)
|
|
12
|
+
# In production, use Alembic migrations
|
|
13
|
+
async with engine.begin() as conn:
|
|
14
|
+
await conn.run_sync(Base.metadata.create_all)
|
|
15
|
+
yield
|
|
16
|
+
# Cleanup
|
|
17
|
+
await engine.dispose()
|
|
18
|
+
|
|
19
|
+
app = FastAPI(
|
|
20
|
+
title="{{projectName}} - Clean Architecture",
|
|
21
|
+
lifespan=lifespan
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
app.include_router(auth_controller.router, prefix=settings.API_V1_STR + "/auth", tags=["Auth"])
|
|
7
25
|
|
|
8
26
|
@app.get("/health")
|
|
9
27
|
def health():
|
|
10
|
-
return {
|
|
28
|
+
return {
|
|
29
|
+
"status": "ok",
|
|
30
|
+
"architecture": "clean",
|
|
31
|
+
"project": settings.PROJECT_NAME
|
|
32
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
fastapi>=0.109.0
|
|
2
2
|
uvicorn[standard]>=0.27.0
|
|
3
3
|
pydantic>=2.5.0
|
|
4
|
+
pydantic-settings>=2.1.0
|
|
4
5
|
python-dotenv>=1.0.0
|
|
5
6
|
python-jose[cryptography]>=3.3.0
|
|
6
7
|
passlib[bcrypt]>=1.7.4
|
|
7
8
|
stripe>=8.0.0
|
|
8
9
|
sqlalchemy>=2.0.0
|
|
9
10
|
alembic>=1.13.0
|
|
11
|
+
asyncpg>=0.29.0
|
|
10
12
|
psycopg2-binary>=2.9.9
|
|
11
13
|
pytest>=8.0.0
|
|
12
|
-
httpx>=0.26.0
|
|
14
|
+
httpx>=0.26.0
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
from fastapi import APIRouter, HTTPException
|
|
1
|
+
from fastapi import APIRouter, HTTPException, Depends
|
|
2
2
|
from pydantic import BaseModel, EmailStr
|
|
3
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
4
|
from app.core.ports import IAuthPort
|
|
5
|
+
from app.core.domain.user import User as DomainUser
|
|
6
|
+
from app.infrastructure.database.session import get_db
|
|
7
|
+
from app.infrastructure.database.models import UserModel
|
|
8
|
+
from app.adapters.outbound.postgres_user_repository import PostgresUserRepository
|
|
9
|
+
from app.core.service import AuthService
|
|
10
|
+
from app.infrastructure.security.adapters import BcryptHasher, JwtTokenGenerator
|
|
4
11
|
|
|
5
12
|
router = APIRouter()
|
|
6
13
|
|
|
@@ -9,21 +16,38 @@ class RegisterRequest(BaseModel):
|
|
|
9
16
|
name: str
|
|
10
17
|
password: str
|
|
11
18
|
|
|
19
|
+
# Dependency Injection Factory
|
|
20
|
+
def get_auth_service(db: AsyncSession = Depends(get_db)) -> IAuthPort:
|
|
21
|
+
repo = PostgresUserRepository(db)
|
|
22
|
+
hasher = BcryptHasher()
|
|
23
|
+
token_gen = JwtTokenGenerator()
|
|
24
|
+
return AuthService(repo, hasher, token_gen)
|
|
25
|
+
|
|
26
|
+
@router.post("/register")
|
|
27
|
+
async def register(
|
|
28
|
+
req: RegisterRequest,
|
|
29
|
+
service: IAuthPort = Depends(get_auth_service)
|
|
30
|
+
):
|
|
31
|
+
try:
|
|
32
|
+
result = await service.register(req.email, req.name, req.password)
|
|
33
|
+
return result
|
|
34
|
+
except ValueError as e:
|
|
35
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
36
|
+
|
|
37
|
+
@router.post("/login")
|
|
38
|
+
async def login(
|
|
39
|
+
req: RegisterRequest,
|
|
40
|
+
service: IAuthPort = Depends(get_auth_service)
|
|
41
|
+
):
|
|
42
|
+
try:
|
|
43
|
+
result = await service.login(req.email, req.password)
|
|
44
|
+
return result
|
|
45
|
+
except ValueError as e:
|
|
46
|
+
raise HTTPException(status_code=401, detail=str(e))
|
|
47
|
+
|
|
48
|
+
# Setup function for backward compatibility or direct usage
|
|
49
|
+
# though router is preferred
|
|
12
50
|
def setup_auth_routes(auth_service: IAuthPort):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
try:
|
|
16
|
-
result = await auth_service.register(req.email, req.name, req.password)
|
|
17
|
-
return result
|
|
18
|
-
except ValueError as e:
|
|
19
|
-
raise HTTPException(status_code=400, detail=str(e))
|
|
20
|
-
|
|
21
|
-
@router.post("/login")
|
|
22
|
-
async def login(req: RegisterRequest):
|
|
23
|
-
try:
|
|
24
|
-
result = await auth_service.login(req.email, req.password)
|
|
25
|
-
return result
|
|
26
|
-
except ValueError as e:
|
|
27
|
-
raise HTTPException(status_code=401, detail=str(e))
|
|
28
|
-
|
|
51
|
+
# This pattern is tricky with FastAPIs dependency injection system
|
|
52
|
+
# It's better to rely on Depends() as implemented above
|
|
29
53
|
return router
|
package/templates/python-fastapi/hexagonal/app/adapters/outbound/postgres_user_repository.py.hbs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
3
|
+
from sqlalchemy import select
|
|
4
|
+
from app.core.ports import IUserRepositoryPort
|
|
5
|
+
from app.core.domain.user import User
|
|
6
|
+
from app.infrastructure.database.models import UserModel
|
|
7
|
+
|
|
8
|
+
class PostgresUserRepository(IUserRepositoryPort):
|
|
9
|
+
def __init__(self, session: AsyncSession):
|
|
10
|
+
self.session = session
|
|
11
|
+
|
|
12
|
+
def _to_entity(self, model: UserModel) -> User:
|
|
13
|
+
return User(
|
|
14
|
+
id=model.id,
|
|
15
|
+
email=model.email,
|
|
16
|
+
name=model.name,
|
|
17
|
+
password=model.password,
|
|
18
|
+
stripe_customer_id=model.stripe_customer_id,
|
|
19
|
+
created_at=model.created_at
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
async def find_by_email(self, email: str) -> Optional[User]:
|
|
23
|
+
result = await self.session.execute(select(UserModel).where(UserModel.email == email))
|
|
24
|
+
model = result.scalars().first()
|
|
25
|
+
return self._to_entity(model) if model else None
|
|
26
|
+
|
|
27
|
+
async def save(self, user: User) -> User:
|
|
28
|
+
# Simplistic save/update logic
|
|
29
|
+
result = await self.session.execute(select(UserModel).where(UserModel.id == user.id))
|
|
30
|
+
model = result.scalars().first()
|
|
31
|
+
|
|
32
|
+
if model:
|
|
33
|
+
model.email = user.email
|
|
34
|
+
model.name = user.name
|
|
35
|
+
model.password = user.password
|
|
36
|
+
model.stripe_customer_id = user.stripe_customer_id
|
|
37
|
+
else:
|
|
38
|
+
model = UserModel(
|
|
39
|
+
id=user.id,
|
|
40
|
+
email=user.email,
|
|
41
|
+
name=user.name,
|
|
42
|
+
password=user.password,
|
|
43
|
+
stripe_customer_id=user.stripe_customer_id,
|
|
44
|
+
created_at=user.created_at
|
|
45
|
+
)
|
|
46
|
+
self.session.add(model)
|
|
47
|
+
|
|
48
|
+
await self.session.commit()
|
|
49
|
+
await self.session.refresh(model)
|
|
50
|
+
return self._to_entity(model)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
|
|
4
|
+
class Settings(BaseSettings):
|
|
5
|
+
PROJECT_NAME: str = "{{projectName}}"
|
|
6
|
+
API_V1_STR: str = "/api/v1"
|
|
7
|
+
|
|
8
|
+
# Database
|
|
9
|
+
DATABASE_URL: str = "postgresql+asyncpg://postgres:postgres@localhost:5432/{{projectName}}_db"
|
|
10
|
+
|
|
11
|
+
# Security
|
|
12
|
+
SECRET_KEY: str = "change_this_to_a_secure_random_key"
|
|
13
|
+
ALGORITHM: str = "HS256"
|
|
14
|
+
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
|
|
15
|
+
|
|
16
|
+
model_config = SettingsConfigDict(env_file=".env", case_sensitive=True)
|
|
17
|
+
|
|
18
|
+
@lru_cache
|
|
19
|
+
def get_settings():
|
|
20
|
+
return Settings()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from sqlalchemy import Column, String, DateTime, Boolean
|
|
2
|
+
from sqlalchemy.sql import func
|
|
3
|
+
from app.infrastructure.database.session import Base
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
class UserModel(Base):
|
|
7
|
+
__tablename__ = "users"
|
|
8
|
+
|
|
9
|
+
id = Column(String, primary_key=True, index=True, default=lambda: str(uuid.uuid4()))
|
|
10
|
+
email = Column(String, unique=True, index=True, nullable=False)
|
|
11
|
+
name = Column(String, nullable=False)
|
|
12
|
+
password = Column(String, nullable=False)
|
|
13
|
+
stripe_customer_id = Column(String, nullable=True)
|
|
14
|
+
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
15
|
+
is_active = Column(Boolean, default=True)
|
|
16
|
+
|
|
17
|
+
def to_dict(self):
|
|
18
|
+
return {
|
|
19
|
+
"id": self.id,
|
|
20
|
+
"email": self.email,
|
|
21
|
+
"name": self.name,
|
|
22
|
+
"stripe_customer_id": self.stripe_customer_id,
|
|
23
|
+
"created_at": self.created_at
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
|
2
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
3
|
+
from app.config import get_settings
|
|
4
|
+
|
|
5
|
+
settings = get_settings()
|
|
6
|
+
|
|
7
|
+
engine = create_async_engine(
|
|
8
|
+
settings.DATABASE_URL,
|
|
9
|
+
echo=True,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
AsyncSessionLocal = async_sessionmaker(
|
|
13
|
+
bind=engine,
|
|
14
|
+
class_=AsyncSession,
|
|
15
|
+
expire_on_commit=False,
|
|
16
|
+
autoflush=False,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
class Base(DeclarativeBase):
|
|
20
|
+
pass
|
|
@@ -1,22 +1,30 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
1
2
|
from fastapi import FastAPI
|
|
2
|
-
from app.
|
|
3
|
-
from app.
|
|
3
|
+
from app.adapters.inbound.http_adapter import router as auth_router
|
|
4
|
+
from app.infrastructure.database.session import engine, Base
|
|
5
|
+
from app.config import get_settings
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
settings = get_settings()
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
@asynccontextmanager
|
|
10
|
+
async def lifespan(app: FastAPI):
|
|
11
|
+
# Create tables on startup (for development)
|
|
12
|
+
async with engine.begin() as conn:
|
|
13
|
+
await conn.run_sync(Base.metadata.create_all)
|
|
14
|
+
yield
|
|
15
|
+
await engine.dispose()
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
app = FastAPI(
|
|
18
|
+
title="{{projectName}} - Hexagonal Architecture",
|
|
19
|
+
lifespan=lifespan
|
|
20
|
+
)
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
auth_router = setup_auth_routes(auth_service)
|
|
18
|
-
app.include_router(auth_router, prefix="/api/auth", tags=["Auth"])
|
|
22
|
+
app.include_router(auth_router, prefix=settings.API_V1_STR + "/auth", tags=["Auth"])
|
|
19
23
|
|
|
20
24
|
@app.get("/health")
|
|
21
25
|
def health():
|
|
22
|
-
return {
|
|
26
|
+
return {
|
|
27
|
+
"status": "ok",
|
|
28
|
+
"architecture": "hexagonal",
|
|
29
|
+
"project": settings.PROJECT_NAME
|
|
30
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
fastapi>=0.109.0
|
|
2
2
|
uvicorn[standard]>=0.27.0
|
|
3
3
|
pydantic>=2.5.0
|
|
4
|
+
pydantic-settings>=2.1.0
|
|
4
5
|
python-dotenv>=1.0.0
|
|
5
6
|
python-jose[cryptography]>=3.3.0
|
|
6
7
|
passlib[bcrypt]>=1.7.4
|
|
7
8
|
stripe>=8.0.0
|
|
8
9
|
sqlalchemy>=2.0.0
|
|
9
10
|
alembic>=1.13.0
|
|
11
|
+
asyncpg>=0.29.0
|
|
10
12
|
psycopg2-binary>=2.9.9
|
|
11
13
|
pytest>=8.0.0
|
|
12
|
-
httpx>=0.26.0
|
|
14
|
+
httpx>=0.26.0
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
The ISC License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2017-2023 npm, Inc., Isaac Z. Schlueter, and Contributors
|
|
4
|
-
|
|
5
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
-
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
-
copyright notice and this permission notice appear in all copies.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
-
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
-
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
-
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
-
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
-
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|
15
|
-
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|