fastforge-cli 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. fastforge/__init__.py +1 -0
  2. fastforge/cli.py +693 -0
  3. fastforge/infra_template/cookiecutter.json +11 -0
  4. fastforge/infra_template/hooks/post_gen_project.py +77 -0
  5. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/README.md +41 -0
  6. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.app.yml +27 -0
  7. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.elasticsearch.yml +32 -0
  8. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.fluentbit.yml +14 -0
  9. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.kafka.yml +20 -0
  10. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.logstash.yml +14 -0
  11. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.mongodb.yml +17 -0
  12. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.postgres.yml +21 -0
  13. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.vault.yml +19 -0
  14. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.vector-agent.yml +13 -0
  15. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.vector-aggregator.yml +9 -0
  16. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/docker-compose.yml +31 -0
  17. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/fluentbit/fluent-bit.conf +24 -0
  18. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/fluentbit/parsers.conf +5 -0
  19. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/logstash/pipeline/logstash.conf +31 -0
  20. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/vault/config.hcl +10 -0
  21. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/vault/policies/app-policy.hcl +7 -0
  22. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/vector/vector-agent.toml +36 -0
  23. fastforge/infra_template/{{cookiecutter.project_slug}}-infrastructure/vector/vector-aggregator.toml +29 -0
  24. fastforge/template/cookiecutter.json +34 -0
  25. fastforge/template/hooks/post_gen_project.py +71 -0
  26. fastforge/template/{{cookiecutter.project_slug}}/.codeclimate.yml +40 -0
  27. fastforge/template/{{cookiecutter.project_slug}}/.dockerignore +15 -0
  28. fastforge/template/{{cookiecutter.project_slug}}/.env.staging +86 -0
  29. fastforge/template/{{cookiecutter.project_slug}}/.gitignore +15 -0
  30. fastforge/template/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +16 -0
  31. fastforge/template/{{cookiecutter.project_slug}}/Dockerfile +30 -0
  32. fastforge/template/{{cookiecutter.project_slug}}/README.md +254 -0
  33. fastforge/template/{{cookiecutter.project_slug}}/app/__init__.py +0 -0
  34. fastforge/template/{{cookiecutter.project_slug}}/app/api/__init__.py +0 -0
  35. fastforge/template/{{cookiecutter.project_slug}}/app/api/exception_handlers.py +78 -0
  36. fastforge/template/{{cookiecutter.project_slug}}/app/api/models/__init__.py +0 -0
  37. fastforge/template/{{cookiecutter.project_slug}}/app/api/models/{{cookiecutter.model_name}}.py +22 -0
  38. fastforge/template/{{cookiecutter.project_slug}}/app/api/routes/__init__.py +0 -0
  39. fastforge/template/{{cookiecutter.project_slug}}/app/api/routes/health.py +20 -0
  40. fastforge/template/{{cookiecutter.project_slug}}/app/api/routes/{{cookiecutter.model_name_plural}}.py +70 -0
  41. fastforge/template/{{cookiecutter.project_slug}}/app/cache.py +63 -0
  42. fastforge/template/{{cookiecutter.project_slug}}/app/config.py +112 -0
  43. fastforge/template/{{cookiecutter.project_slug}}/app/db/__init__.py +0 -0
  44. fastforge/template/{{cookiecutter.project_slug}}/app/db/models/__init__.py +0 -0
  45. fastforge/template/{{cookiecutter.project_slug}}/app/db/models/{{cookiecutter.model_name}}.py +34 -0
  46. fastforge/template/{{cookiecutter.project_slug}}/app/db/mongodb.py +23 -0
  47. fastforge/template/{{cookiecutter.project_slug}}/app/db/sqlalchemy.py +14 -0
  48. fastforge/template/{{cookiecutter.project_slug}}/app/dependencies.py +45 -0
  49. fastforge/template/{{cookiecutter.project_slug}}/app/logging_config.py +84 -0
  50. fastforge/template/{{cookiecutter.project_slug}}/app/main.py +106 -0
  51. fastforge/template/{{cookiecutter.project_slug}}/app/middleware/__init__.py +0 -0
  52. fastforge/template/{{cookiecutter.project_slug}}/app/middleware/logging_middleware.py +45 -0
  53. fastforge/template/{{cookiecutter.project_slug}}/app/middleware/security_headers.py +18 -0
  54. fastforge/template/{{cookiecutter.project_slug}}/app/repositories/__init__.py +0 -0
  55. fastforge/template/{{cookiecutter.project_slug}}/app/repositories/{{cookiecutter.model_name}}_repository.py +172 -0
  56. fastforge/template/{{cookiecutter.project_slug}}/app/secrets.py +101 -0
  57. fastforge/template/{{cookiecutter.project_slug}}/app/services/__init__.py +0 -0
  58. fastforge/template/{{cookiecutter.project_slug}}/app/services/{{cookiecutter.model_name}}_service.py +63 -0
  59. fastforge/template/{{cookiecutter.project_slug}}/app/streaming/__init__.py +0 -0
  60. fastforge/template/{{cookiecutter.project_slug}}/app/streaming/consumer.py +187 -0
  61. fastforge/template/{{cookiecutter.project_slug}}/app/streaming/handler.py +31 -0
  62. fastforge/template/{{cookiecutter.project_slug}}/app/streaming/producer.py +151 -0
  63. fastforge/template/{{cookiecutter.project_slug}}/docker-compose.debug.yml +15 -0
  64. fastforge/template/{{cookiecutter.project_slug}}/docker-compose.yml +141 -0
  65. fastforge/template/{{cookiecutter.project_slug}}/pyproject.toml +100 -0
  66. fastforge/template/{{cookiecutter.project_slug}}/qodana.yaml +22 -0
  67. fastforge/template/{{cookiecutter.project_slug}}/sonar-project.properties +20 -0
  68. fastforge/template/{{cookiecutter.project_slug}}/tests/__init__.py +0 -0
  69. fastforge/template/{{cookiecutter.project_slug}}/tests/conftest.py +11 -0
  70. fastforge/template/{{cookiecutter.project_slug}}/tests/test_api.py +51 -0
  71. fastforge_cli-0.0.1.dist-info/METADATA +163 -0
  72. fastforge_cli-0.0.1.dist-info/RECORD +76 -0
  73. fastforge_cli-0.0.1.dist-info/WHEEL +5 -0
  74. fastforge_cli-0.0.1.dist-info/entry_points.txt +12 -0
  75. fastforge_cli-0.0.1.dist-info/licenses/LICENSE +21 -0
  76. fastforge_cli-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,11 @@
1
+ {
2
+ "project_slug": "my-fastapi-service",
3
+
4
+ "log_agent": ["none", "vector", "fluentbit"],
5
+ "log_aggregator": ["none", "vector", "logstash"],
6
+ "streaming": ["none", "enabled"],
7
+ "secrets": ["none", "vault"],
8
+ "database": ["none", "postgres", "mongodb"],
9
+
10
+ "_copy_without_render": []
11
+ }
@@ -0,0 +1,77 @@
1
+ """Post-generation hook for infrastructure template — removes unused service files."""
2
+
3
+ import os
4
+ import shutil
5
+
6
+ PROJECT_DIR = os.path.realpath(os.path.curdir)
7
+
8
+
9
+ def remove_path(rel_path: str) -> None:
10
+ abs_path = os.path.join(PROJECT_DIR, rel_path)
11
+ if os.path.isdir(abs_path):
12
+ shutil.rmtree(abs_path)
13
+ elif os.path.isfile(abs_path):
14
+ os.remove(abs_path)
15
+
16
+
17
+ # ── Log Agent ────────────────────────────────────────────────────────────────
18
+ log_agent = "{{ cookiecutter.log_agent }}"
19
+ log_aggregator = "{{ cookiecutter.log_aggregator }}"
20
+
21
+ if log_agent == "none":
22
+ remove_path("docker-compose.vector-agent.yml")
23
+ remove_path("docker-compose.fluentbit.yml")
24
+ elif log_agent == "vector":
25
+ remove_path("docker-compose.fluentbit.yml")
26
+ remove_path("fluentbit")
27
+ elif log_agent == "fluentbit":
28
+ remove_path("docker-compose.vector-agent.yml")
29
+ remove_path("vector/vector-agent.toml")
30
+
31
+ # ── Log Aggregator ───────────────────────────────────────────────────────────
32
+ if log_aggregator == "none":
33
+ remove_path("docker-compose.vector-aggregator.yml")
34
+ remove_path("docker-compose.logstash.yml")
35
+ remove_path("docker-compose.elasticsearch.yml")
36
+ elif log_aggregator == "vector":
37
+ remove_path("docker-compose.logstash.yml")
38
+ remove_path("logstash")
39
+ elif log_aggregator == "logstash":
40
+ remove_path("docker-compose.vector-aggregator.yml")
41
+ remove_path("vector/vector-aggregator.toml")
42
+
43
+ # Clean up empty vector dir if neither uses it
44
+ if log_agent != "vector" and log_aggregator != "vector":
45
+ remove_path("vector")
46
+
47
+ # Remove FluentBit if not used as agent
48
+ if log_agent != "fluentbit":
49
+ remove_path("fluentbit")
50
+
51
+ # Remove Logstash if not used as aggregator
52
+ if log_aggregator != "logstash":
53
+ remove_path("logstash")
54
+
55
+ # No log pipeline means no ES/Kibana needed
56
+ if log_agent == "none" and log_aggregator == "none":
57
+ remove_path("docker-compose.elasticsearch.yml")
58
+
59
+ # ── Kafka ────────────────────────────────────────────────────────────────────
60
+ # Kafka needed for streaming OR as log transport
61
+ needs_kafka = "{{ cookiecutter.streaming }}" != "none" or log_agent != "none"
62
+ if not needs_kafka:
63
+ remove_path("docker-compose.kafka.yml")
64
+
65
+ # ── Database ─────────────────────────────────────────────────────────────────
66
+ if "{{ cookiecutter.database }}" != "postgres":
67
+ remove_path("docker-compose.postgres.yml")
68
+ if "{{ cookiecutter.database }}" != "mongodb":
69
+ remove_path("docker-compose.mongodb.yml")
70
+
71
+ # ── Vault ────────────────────────────────────────────────────────────────────
72
+ if "{{ cookiecutter.secrets }}" != "vault":
73
+ remove_path("docker-compose.vault.yml")
74
+ remove_path("vault")
75
+
76
+ print("\n✅ Infrastructure stack generated: {{ cookiecutter.project_slug }}-infrastructure/")
77
+ print(" cd {{ cookiecutter.project_slug }}-infrastructure && docker compose up -d")
@@ -0,0 +1,41 @@
1
+ # {{ cookiecutter.project_slug }} — Infrastructure Stack
2
+
3
+ Docker Compose stack for supporting services (local development / staging).
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ docker compose up -d
9
+ ```
10
+
11
+ ## Services
12
+
13
+ | Service | Port | Description |
14
+ |---------|------|-------------|
15
+ | App | 8000 | FastAPI application |
16
+ {%- if cookiecutter.log_agent == "vector" %}
17
+ | Vector Agent | — | Log collector (file → Kafka) |
18
+ {%- elif cookiecutter.log_agent == "fluentbit" %}
19
+ | Fluent Bit | — | Log collector (file → Kafka) |
20
+ {%- endif %}
21
+ {%- if cookiecutter.log_aggregator == "vector" %}
22
+ | Vector Aggregator | — | Log pipeline (Kafka → Elasticsearch) |
23
+ {%- elif cookiecutter.log_aggregator == "logstash" %}
24
+ | Logstash | — | Log pipeline (Kafka → Elasticsearch) |
25
+ {%- endif %}
26
+ {%- if cookiecutter.log_agent != "none" or cookiecutter.log_aggregator != "none" %}
27
+ | Elasticsearch | 9200 | Log storage |
28
+ | Kibana | 5601 | Log visualization |
29
+ {%- endif %}
30
+ {%- if cookiecutter.streaming != "none" or cookiecutter.log_agent != "none" %}
31
+ | Kafka | 9092 | Message broker |
32
+ {%- endif %}
33
+ {%- if cookiecutter.database == "postgres" %}
34
+ | PostgreSQL | 5432 | Database |
35
+ {%- endif %}
36
+ {%- if cookiecutter.database == "mongodb" %}
37
+ | MongoDB | 27017 | Database |
38
+ {%- endif %}
39
+ {%- if cookiecutter.secrets == "vault" %}
40
+ | Vault | 8200 | Secret management |
41
+ {%- endif %}
@@ -0,0 +1,27 @@
1
+ services:
2
+ app:
3
+ build:
4
+ context: ..
5
+ dockerfile: Dockerfile
6
+ container_name: {{ cookiecutter.project_slug }}-app
7
+ ports:
8
+ - "8000:8000"
9
+ environment:
10
+ - APP_ENV=development
11
+ volumes:
12
+ {%- if cookiecutter.log_agent != "none" %}
13
+ - app-logs:/var/log/app
14
+ {%- endif %}
15
+ - ../app:/code/app:ro
16
+ healthcheck:
17
+ test: ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"]
18
+ interval: 15s
19
+ timeout: 5s
20
+ retries: 3
21
+
22
+ {%- if cookiecutter.log_agent != "none" %}
23
+
24
+ volumes:
25
+ app-logs:
26
+ name: {{ cookiecutter.project_slug }}-app-logs
27
+ {%- endif %}
@@ -0,0 +1,32 @@
1
+ services:
2
+ elasticsearch:
3
+ image: docker.elastic.co/elasticsearch/elasticsearch:8.15.0
4
+ container_name: {{ cookiecutter.project_slug }}-elasticsearch
5
+ environment:
6
+ - discovery.type=single-node
7
+ - xpack.security.enabled=false
8
+ - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
9
+ ports:
10
+ - "9200:9200"
11
+ volumes:
12
+ - esdata:/var/lib/elasticsearch/data
13
+ healthcheck:
14
+ test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"]
15
+ interval: 30s
16
+ timeout: 10s
17
+ retries: 5
18
+
19
+ kibana:
20
+ image: docker.elastic.co/kibana/kibana:8.15.0
21
+ container_name: {{ cookiecutter.project_slug }}-kibana
22
+ environment:
23
+ - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
24
+ ports:
25
+ - "5601:5601"
26
+ depends_on:
27
+ elasticsearch:
28
+ condition: service_healthy
29
+
30
+ volumes:
31
+ esdata:
32
+ name: {{ cookiecutter.project_slug }}-esdata
@@ -0,0 +1,14 @@
1
+ services:
2
+ fluentbit:
3
+ image: fluent/fluent-bit:3.1
4
+ container_name: {{ cookiecutter.project_slug }}-fluentbit
5
+ volumes:
6
+ - ./fluentbit/fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf:ro
7
+ - ./fluentbit/parsers.conf:/fluent-bit/etc/parsers.conf:ro
8
+ - app-logs:/var/log/app:ro
9
+ depends_on:
10
+ - kafka
11
+
12
+ volumes:
13
+ app-logs:
14
+ name: {{ cookiecutter.project_slug }}-app-logs
@@ -0,0 +1,20 @@
1
+ services:
2
+ kafka:
3
+ image: confluentinc/cp-kafka:7.7.0
4
+ container_name: {{ cookiecutter.project_slug }}-kafka
5
+ environment:
6
+ KAFKA_NODE_ID: 1
7
+ KAFKA_PROCESS_ROLES: broker,controller
8
+ KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
9
+ KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093
10
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
11
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
12
+ KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
13
+ CLUSTER_ID: {{ cookiecutter.project_slug }}-kafka-cluster
14
+ ports:
15
+ - "9092:9092"
16
+ healthcheck:
17
+ test: ["CMD-SHELL", "kafka-broker-api-versions --bootstrap-server localhost:9092"]
18
+ interval: 30s
19
+ timeout: 10s
20
+ retries: 5
@@ -0,0 +1,14 @@
1
+ services:
2
+ logstash:
3
+ image: docker.elastic.co/logstash/logstash:8.15.0
4
+ container_name: {{ cookiecutter.project_slug }}-logstash
5
+ volumes:
6
+ - ./logstash/pipeline:/usr/share/logstash/pipeline:ro
7
+ environment:
8
+ LS_JAVA_OPTS: "-Xms256m -Xmx256m"
9
+ XPACK_MONITORING_ENABLED: "false"
10
+ depends_on:
11
+ kafka:
12
+ condition: service_healthy
13
+ elasticsearch:
14
+ condition: service_healthy
@@ -0,0 +1,17 @@
1
+ services:
2
+ mongodb:
3
+ image: mongo:7
4
+ container_name: {{ cookiecutter.project_slug }}-mongodb
5
+ ports:
6
+ - "27017:27017"
7
+ volumes:
8
+ - mongodata:/data/db
9
+ healthcheck:
10
+ test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
11
+ interval: 10s
12
+ timeout: 5s
13
+ retries: 5
14
+
15
+ volumes:
16
+ mongodata:
17
+ name: {{ cookiecutter.project_slug }}-mongodata
@@ -0,0 +1,21 @@
1
+ services:
2
+ postgres:
3
+ image: postgres:16-alpine
4
+ container_name: {{ cookiecutter.project_slug }}-postgres
5
+ environment:
6
+ POSTGRES_USER: postgres
7
+ POSTGRES_PASSWORD: postgres
8
+ POSTGRES_DB: {{ cookiecutter.project_slug | replace('-', '_') }}
9
+ ports:
10
+ - "5432:5432"
11
+ volumes:
12
+ - pgdata:/var/lib/postgresql/data
13
+ healthcheck:
14
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
15
+ interval: 10s
16
+ timeout: 5s
17
+ retries: 5
18
+
19
+ volumes:
20
+ pgdata:
21
+ name: {{ cookiecutter.project_slug }}-pgdata
@@ -0,0 +1,19 @@
1
+ services:
2
+ vault:
3
+ image: hashicorp/vault:1.17
4
+ container_name: {{ cookiecutter.project_slug }}-vault
5
+ cap_add:
6
+ - IPC_LOCK
7
+ environment:
8
+ VAULT_DEV_ROOT_TOKEN_ID: dev-only-token
9
+ VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
10
+ ports:
11
+ - "8200:8200"
12
+ volumes:
13
+ - ./vault/config.hcl:/vault/config/config.hcl:ro
14
+ - ./vault/policies:/vault/policies:ro
15
+ healthcheck:
16
+ test: ["CMD-SHELL", "vault status || true"]
17
+ interval: 10s
18
+ timeout: 5s
19
+ retries: 3
@@ -0,0 +1,13 @@
1
+ services:
2
+ vector-agent:
3
+ image: timberio/vector:0.41.1-alpine
4
+ container_name: {{ cookiecutter.project_slug }}-vector-agent
5
+ volumes:
6
+ - ./vector/vector-agent.toml:/etc/vector/vector.toml:ro
7
+ - app-logs:/var/log/app:ro
8
+ depends_on:
9
+ - kafka
10
+
11
+ volumes:
12
+ app-logs:
13
+ name: {{ cookiecutter.project_slug }}-app-logs
@@ -0,0 +1,9 @@
1
+ services:
2
+ vector-aggregator:
3
+ image: timberio/vector:0.41.1-alpine
4
+ container_name: {{ cookiecutter.project_slug }}-vector-aggregator
5
+ volumes:
6
+ - ./vector/vector-aggregator.toml:/etc/vector/vector.toml:ro
7
+ depends_on:
8
+ - kafka
9
+ - elasticsearch
@@ -0,0 +1,31 @@
1
+ # Infrastructure Stack — {{ cookiecutter.project_slug }}
2
+ # Usage:
3
+ # docker compose -f infrastructure/docker-compose.yml up -d
4
+
5
+ include:
6
+ - docker-compose.app.yml
7
+ {%- if cookiecutter.log_agent == "vector" %}
8
+ - docker-compose.vector-agent.yml
9
+ {%- elif cookiecutter.log_agent == "fluentbit" %}
10
+ - docker-compose.fluentbit.yml
11
+ {%- endif %}
12
+ {%- if cookiecutter.log_aggregator == "vector" %}
13
+ - docker-compose.vector-aggregator.yml
14
+ {%- elif cookiecutter.log_aggregator == "logstash" %}
15
+ - docker-compose.logstash.yml
16
+ {%- endif %}
17
+ {%- if cookiecutter.log_agent != "none" or cookiecutter.log_aggregator != "none" %}
18
+ - docker-compose.elasticsearch.yml
19
+ {%- endif %}
20
+ {%- if cookiecutter.streaming != "none" or cookiecutter.log_agent != "none" %}
21
+ - docker-compose.kafka.yml
22
+ {%- endif %}
23
+ {%- if cookiecutter.database == "postgres" %}
24
+ - docker-compose.postgres.yml
25
+ {%- endif %}
26
+ {%- if cookiecutter.database == "mongodb" %}
27
+ - docker-compose.mongodb.yml
28
+ {%- endif %}
29
+ {%- if cookiecutter.secrets == "vault" %}
30
+ - docker-compose.vault.yml
31
+ {%- endif %}
@@ -0,0 +1,24 @@
1
+ [SERVICE]
2
+ Flush 1
3
+ Daemon Off
4
+ Log_Level info
5
+ Parsers_File parsers.conf
6
+
7
+ [INPUT]
8
+ Name tail
9
+ Path /var/log/app/*.log
10
+ Tag app.*
11
+ Parser json
12
+ Read_from_Head True
13
+ Refresh_Interval 5
14
+ Rotate_Wait 30
15
+ DB /fluent-bit/etc/tail.db
16
+
17
+ [OUTPUT]
18
+ Name kafka
19
+ Match app.*
20
+ Brokers kafka:9092
21
+ Topics app-logs
22
+ Format json
23
+ Timestamp_Key @timestamp
24
+ Retry_Limit 5
@@ -0,0 +1,5 @@
1
+ [PARSER]
2
+ Name json
3
+ Format json
4
+ Time_Key timestamp
5
+ Time_Format %Y-%m-%dT%H:%M:%S.%L%z
@@ -0,0 +1,31 @@
1
+ input {
2
+ kafka {
3
+ bootstrap_servers => "kafka:9092"
4
+ topics => ["app-logs"]
5
+ group_id => "logstash-aggregator"
6
+ codec => "json"
7
+ consumer_threads => 1
8
+ }
9
+ }
10
+
11
+ filter {
12
+ mutate {
13
+ add_field => {
14
+ "service" => "{{ cookiecutter.project_slug }}"
15
+ "indexed_at" => "%{@timestamp}"
16
+ }
17
+ }
18
+
19
+ if [environment] == "" {
20
+ mutate {
21
+ add_field => { "environment" => "unknown" }
22
+ }
23
+ }
24
+ }
25
+
26
+ output {
27
+ elasticsearch {
28
+ hosts => ["http://elasticsearch:9200"]
29
+ index => "{{ cookiecutter.project_slug }}-logs-%{+YYYY.MM.dd}"
30
+ }
31
+ }
@@ -0,0 +1,10 @@
1
+ storage "file" {
2
+ path = "/vault/file"
3
+ }
4
+
5
+ listener "tcp" {
6
+ address = "0.0.0.0:8200"
7
+ tls_disable = 1
8
+ }
9
+
10
+ ui = true
@@ -0,0 +1,7 @@
1
+ path "secret/data/{{ cookiecutter.project_slug }}/*" {
2
+ capabilities = ["read", "list"]
3
+ }
4
+
5
+ path "secret/metadata/{{ cookiecutter.project_slug }}/*" {
6
+ capabilities = ["list"]
7
+ }
@@ -0,0 +1,36 @@
1
+ # Vector Agent — collects logs from file → forwards to Kafka
2
+
3
+ [sources.app_logs]
4
+ type = "file"
5
+ include = ["/var/log/app/*.log"]
6
+ read_from = "beginning"
7
+ fingerprint.strategy = "device_and_inode"
8
+
9
+ [transforms.parse_json]
10
+ type = "remap"
11
+ inputs = ["app_logs"]
12
+ source = '''
13
+ parsed, err = parse_json(.message)
14
+ if err == null {
15
+ . = merge(., parsed)
16
+ del(.message)
17
+ } else {
18
+ .raw_message = .message
19
+ del(.message)
20
+ }
21
+ .source = "vector-agent"
22
+ .pipeline = "file-to-kafka"
23
+ .log_file = .file
24
+ '''
25
+
26
+ [sinks.kafka]
27
+ type = "kafka"
28
+ inputs = ["parse_json"]
29
+ bootstrap_servers = "kafka:9092"
30
+ topic = "app-logs"
31
+ encoding.codec = "json"
32
+
33
+ [sinks.kafka.buffer]
34
+ type = "disk"
35
+ max_size = 268435456
36
+ when_full = "block"
@@ -0,0 +1,29 @@
1
+ # Vector Aggregator — consumes from Kafka → sinks to Elasticsearch
2
+
3
+ [sources.kafka]
4
+ type = "kafka"
5
+ bootstrap_servers = "kafka:9092"
6
+ group_id = "vector-aggregator"
7
+ topics = ["app-logs"]
8
+ decoding.codec = "json"
9
+
10
+ [transforms.enrich]
11
+ type = "remap"
12
+ inputs = ["kafka"]
13
+ source = '''
14
+ .environment = get_env_var("APP_ENV") ?? "unknown"
15
+ .service = "{{ cookiecutter.project_slug }}"
16
+ .indexed_at = now()
17
+ '''
18
+
19
+ [sinks.elasticsearch]
20
+ type = "elasticsearch"
21
+ inputs = ["enrich"]
22
+ endpoints = ["http://elasticsearch:9200"]
23
+ bulk.index = "{{ cookiecutter.project_slug }}-logs-%Y-%m-%d"
24
+ encoding.except_fields = ["source_type"]
25
+
26
+ [sinks.elasticsearch.buffer]
27
+ type = "disk"
28
+ max_size = 268435456
29
+ when_full = "block"
@@ -0,0 +1,34 @@
1
+ {
2
+ "project_name": "my-fastapi-service",
3
+ "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '-').replace('_', '-') }}",
4
+ "package_name": "{{ cookiecutter.project_slug.replace('-', '_') }}",
5
+ "description": "A production-grade FastAPI service",
6
+ "author_name": "Your Name",
7
+ "author_email": "you@example.com",
8
+ "python_version": "3.11",
9
+ "port": "8000",
10
+
11
+ "model_name": "item",
12
+ "model_name_class": "Item",
13
+ "model_name_plural": "items",
14
+
15
+ "database": ["none", "postgres", "mysql", "sqlite", "mongodb"],
16
+ "cache": ["none", "redis", "memcached", "in_memory"],
17
+ "streaming": ["none", "kafka", "rabbitmq", "redis_pubsub", "nats"],
18
+ "secrets": ["none", "vault", "aws_sm", "azure_kv", "gcp_sm"],
19
+
20
+ "logging": ["structlog", "none"],
21
+ "log_format": ["json", "console"],
22
+ "log_connector": ["stdout", "file"],
23
+
24
+ "quality_gate": ["none", "sonarqube", "sonarcloud", "qodana", "codeclimate"],
25
+
26
+ "docker": ["yes", "no"],
27
+ "docker_debug": ["yes", "no"],
28
+
29
+ "precommit": ["yes", "no"],
30
+
31
+ "_copy_without_render": [
32
+ "*.html"
33
+ ]
34
+ }
@@ -0,0 +1,71 @@
1
+ """Post-generation hook — removes files and directories for disabled features."""
2
+
3
+ import os
4
+ import shutil
5
+
6
+ PROJECT_DIR = os.path.realpath(os.path.curdir)
7
+
8
+
9
+ def remove_path(rel_path: str) -> None:
10
+ abs_path = os.path.join(PROJECT_DIR, rel_path)
11
+ if os.path.isdir(abs_path):
12
+ shutil.rmtree(abs_path)
13
+ elif os.path.isfile(abs_path):
14
+ os.remove(abs_path)
15
+
16
+
17
+ # ── Logging ──────────────────────────────────────────────────────────────────
18
+ if "{{ cookiecutter.logging }}" == "none":
19
+ remove_path("app/logging_config.py")
20
+ remove_path("app/middleware/logging_middleware.py")
21
+
22
+ # ── Database ─────────────────────────────────────────────────────────────────
23
+ database = "{{ cookiecutter.database }}"
24
+ if database == "none":
25
+ remove_path("app/db")
26
+ elif database in ("postgres", "mysql", "sqlite"):
27
+ remove_path("app/db/mongodb.py")
28
+ elif database == "mongodb":
29
+ remove_path("app/db/sqlalchemy.py")
30
+
31
+ # ── Cache ────────────────────────────────────────────────────────────────────
32
+ if "{{ cookiecutter.cache }}" == "none":
33
+ remove_path("app/cache.py")
34
+
35
+ # ── Streaming ────────────────────────────────────────────────────────────────
36
+ if "{{ cookiecutter.streaming }}" == "none":
37
+ remove_path("app/streaming")
38
+
39
+ # ── Secrets ──────────────────────────────────────────────────────────────────
40
+ if "{{ cookiecutter.secrets }}" == "none":
41
+ remove_path("app/secrets.py")
42
+
43
+ # ── Quality Gate ─────────────────────────────────────────────────────────────
44
+ quality_gate = "{{ cookiecutter.quality_gate }}"
45
+ if quality_gate != "sonarqube" and quality_gate != "sonarcloud":
46
+ remove_path("sonar-project.properties")
47
+ if quality_gate != "qodana":
48
+ remove_path("qodana.yaml")
49
+ if quality_gate != "codeclimate":
50
+ remove_path(".codeclimate.yml")
51
+
52
+ # ── Docker ───────────────────────────────────────────────────────────────────
53
+ if "{{ cookiecutter.docker }}" != "yes":
54
+ remove_path("Dockerfile")
55
+ remove_path(".dockerignore")
56
+ remove_path("docker-compose.yml")
57
+ remove_path("docker-compose.debug.yml")
58
+ elif "{{ cookiecutter.docker_debug }}" != "yes":
59
+ remove_path("docker-compose.debug.yml")
60
+
61
+ # ── Pre-commit ───────────────────────────────────────────────────────────────
62
+ if "{{ cookiecutter.precommit }}" != "yes":
63
+ remove_path(".pre-commit-config.yaml")
64
+
65
+ # ── Init git repo ────────────────────────────────────────────────────────────
66
+ os.system("git init -q")
67
+ os.system("git add .")
68
+ os.system('git commit -q -m "Initial project from FastForge"')
69
+
70
+ print("\n✅ {{ cookiecutter.project_name }} generated successfully!")
71
+ print(" cd {{ cookiecutter.project_slug }} && pip install -e '.[dev]'")
@@ -0,0 +1,40 @@
1
+ version: "2"
2
+
3
+ plugins:
4
+ pep8:
5
+ enabled: true
6
+ radon:
7
+ enabled: true
8
+ config:
9
+ threshold: "C"
10
+ bandit:
11
+ enabled: true
12
+
13
+ checks:
14
+ argument-count:
15
+ config:
16
+ threshold: 5
17
+ complex-logic:
18
+ config:
19
+ threshold: 4
20
+ file-lines:
21
+ config:
22
+ threshold: 300
23
+ method-complexity:
24
+ config:
25
+ threshold: 10
26
+ method-count:
27
+ config:
28
+ threshold: 15
29
+ method-lines:
30
+ config:
31
+ threshold: 30
32
+ return-statements:
33
+ config:
34
+ threshold: 4
35
+
36
+ exclude_patterns:
37
+ - "tests/"
38
+ - ".venv/"
39
+ - "__pycache__/"
40
+ - "migrations/"