diff --git a/.github/workflows/sld-api-docker-image.yml b/.github/workflows/sld-api-docker-image.yml index d414c4a0..0171fbc8 100644 --- a/.github/workflows/sld-api-docker-image.yml +++ b/.github/workflows/sld-api-docker-image.yml @@ -24,11 +24,11 @@ jobs: - name: Build the Docker image with tag working-directory: ./sld-api-backend - run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-api:2.17.0 + run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-api:2.18.0 - name: Docker Push with tag #if: github.event.pull_request.merged == true - run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-api:2.17.0 + run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-api:2.18.0 - name: Build the Docker image working-directory: ./sld-api-backend diff --git a/.github/workflows/sld-dashboard-docker-image.yml b/.github/workflows/sld-dashboard-docker-image.yml index 739eb3fd..d16b9ee6 100644 --- a/.github/workflows/sld-dashboard-docker-image.yml +++ b/.github/workflows/sld-dashboard-docker-image.yml @@ -24,11 +24,11 @@ jobs: - name: Build the Docker image with tags working-directory: ./sld-dashboard - run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-dashboard:2.15.0 + run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-dashboard:2.16.0 - name: Docker Push with tags #if: github.event.pull_request.merged == true - run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-dashboard:2.15.0 + run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-dashboard:2.16.0 - name: Build the Docker image working-directory: ./sld-dashboard diff --git a/.github/workflows/sld-remote-state-docker-image.yml b/.github/workflows/sld-remote-state-docker-image.yml index c900028e..7892887c 100644 --- a/.github/workflows/sld-remote-state-docker-image.yml +++ b/.github/workflows/sld-remote-state-docker-image.yml @@ -24,11 +24,11 @@ jobs: - name: Build the Docker image with tags working-directory: ./sld-remote-state - run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-remote-state:2.10.0 + run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-remote-state:2.11.0 - name: Docker Push with tags #if: github.event.pull_request.merged == true - run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-remote-state:2.10.0 + run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-remote-state:2.11.0 - name: Build the Docker image working-directory: ./sld-remote-state diff --git a/.github/workflows/sld-schedule-docker-image.yml b/.github/workflows/sld-schedule-docker-image.yml index d8185d32..8a43b30e 100644 --- a/.github/workflows/sld-schedule-docker-image.yml +++ b/.github/workflows/sld-schedule-docker-image.yml @@ -24,11 +24,11 @@ jobs: - name: Build the Docker image with tags working-directory: ./sld-schedule - run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-schedule:2.6.0 + run: docker build . --file Dockerfile --tag ${{ secrets.DOCKER_USERNAME }}/sld-schedule:2.7.0 - name: Docker Push with tags #if: github.event.pull_request.merged == true - run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-schedule:2.6.0 + run: docker push ${{ secrets.DOCKER_USERNAME }}/sld-schedule:2.7.0 - name: Build the Docker image working-directory: ./sld-schedule diff --git a/sld-api-backend/.tool-versions b/sld-api-backend/.tool-versions new file mode 100644 index 00000000..b4736d5d --- /dev/null +++ b/sld-api-backend/.tool-versions @@ -0,0 +1 @@ +python 3.11.6 diff --git a/sld-api-backend/Dockerfile b/sld-api-backend/Dockerfile index 614290df..40173997 100644 --- a/sld-api-backend/Dockerfile +++ b/sld-api-backend/Dockerfile @@ -1,32 +1,54 @@ FROM ubuntu:22.04 -MAINTAINER D10S0VSkY +# Metadata +MAINTAINER D10S0VSkY ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 ENV TZ=Europe/Madrid +# Set up working directory WORKDIR /app ADD ./requirements.txt /app/requirements.txt +# Create a user and group RUN groupadd --gid 10000 sld && \ - useradd --uid 10000 --gid sld --shell /bin/bash --create-home sld + useradd --uid 10000 --gid sld --shell /bin/bash --create-home sld +# Set timezone RUN echo $TZ > /etc/timezone && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime - +# Install dependencies including build tools RUN export DEBIAN_FRONTEND=noninteractive && \ -apt-get update && \ -apt-get upgrade -yq && \ -apt-get -yq install \ -python3.10 pip default-libmysqlclient-dev zip git tzdata && \ -pip install --no-cache-dir -r requirements.txt + apt-get update && \ + apt-get -yq install curl git zip tzdata build-essential libssl-dev libffi-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev pkg-config libmysqlclient-dev -RUN apt-get clean autoclean && \ -apt-get autoremove -y && \ -rm -rf /var/lib/{apt,dpkg,cache,log}/ +# Install asdf, Python plugin, and Python version +RUN git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.0 && \ + echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc && \ + echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc + +SHELL ["/bin/bash", "-c"] + +RUN . $HOME/.asdf/asdf.sh && \ + asdf plugin add python && \ + asdf install python 3.11.6 && \ + asdf global python 3.11.6 + + +# Install Python packages +RUN . $HOME/.asdf/asdf.sh && \ + python -m pip install --upgrade pip setuptools && \ + python -m pip install --no-cache-dir -r requirements.txt + +# Clean up +RUN apt-get clean autoclean && \ + apt-get autoremove -y && \ + rm -rf /var/lib/{apt,dpkg,cache,log} +# Add the rest of the application ADD . /app/ RUN chown -R sld /app +# Switch to user USER sld \ No newline at end of file diff --git a/sld-api-backend/config/api.py b/sld-api-backend/config/api.py index 93940aef..98a21dd8 100644 --- a/sld-api-backend/config/api.py +++ b/sld-api-backend/config/api.py @@ -1,8 +1,7 @@ import os from typing import List -from pydantic import BaseSettings - +from pydantic_settings import BaseSettings class Settings(BaseSettings): # Schedle config @@ -23,7 +22,7 @@ class Settings(BaseSettings): "SLD_SECRET_KEY", "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7", ) - ALGORITHM = "HS256" + ALGORITHM: str = "HS256" # 60 minutes * 24 hours * 8 days = 8 days ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 SECRET_VAULT: bytes = os.getenv( @@ -36,11 +35,11 @@ class Settings(BaseSettings): WORKER_TMOUT: int = os.getenv("SLD_WORKER_TMOUT", 300) ENV: str = os.getenv("SLD_ENV", "dev") DEBUG: bool = os.getenv("SLD_DEBUG", False) - BACKEND_USER = os.getenv("BACKEND_USER", "") - BACKEND_PASSWD = os.getenv("BACKEND_PASSWD", "") - BACKEND_SERVER = os.getenv("BACKEND_SERVER", "redis") + BACKEND_USER: str = os.getenv("BACKEND_USER", "") + BACKEND_PASSWD: str = os.getenv("BACKEND_PASSWD", "") + BACKEND_SERVER: str = os.getenv("BACKEND_SERVER", "redis") # init user - INIT_USER = { + INIT_USER: dict = { "username": os.getenv("SLD_INIT_USER_NAME", "admin"), "fullname": os.getenv("SLD_INIT_USER_FULLNAME", "Master of the universe user"), "email": os.getenv("SLD_INIT_USER_email", "admin@example.com"), @@ -50,7 +49,7 @@ class Settings(BaseSettings): AWS_SHARED_CONFIG_FILE: str = f"{AWS_CONGIG_DEFAULT_FOLDER}/config" TASK_MAX_RETRY: int = os.getenv("SLD_TASK_MAX_RETRY", 1) TASK_RETRY_INTERVAL: int = os.getenv("SLD_TASK_RETRY_INTERVAL", 20) - TASK_LOCKED_EXPIRED = os.getenv("SLD_TASK_LOCKED_EXPIRED", 300) + TASK_LOCKED_EXPIRED: int = os.getenv("SLD_TASK_LOCKED_EXPIRED", 300) TASK_ROUTE: bool = os.getenv("SLD_TASK_ROUTE", False) TERRAFORM_BIN_REPO: str = os.getenv( "SLD_TERRAFORM_BIN_REPO", "https://releases.hashicorp.com/terraform" diff --git a/sld-api-backend/config/celery_config.py b/sld-api-backend/config/celery_config.py index 9262dd41..be4392ae 100644 --- a/sld-api-backend/config/celery_config.py +++ b/sld-api-backend/config/celery_config.py @@ -4,13 +4,13 @@ celery_app = None # Rabbit broker config -BROKER_USER = os.getenv("BROKER_USER", "admin") -BROKER_PASSWD = os.getenv("BROKER_PASSWD", "admin") -BROKER_SERVER = os.getenv("BROKER_SERVER", "rabbit") # use rabbit or redis +BROKER_USER = os.getenv("BROKER_USER", "") +BROKER_PASSWD = os.getenv("BROKER_PASSWD", "") +BROKER_SERVER = os.getenv("BROKER_SERVER", "redis") # use rabbit or redis BROKER_SERVER_PORT = os.getenv( - "BROKER_SERVER_PORT", "5672" + "BROKER_SERVER_PORT", "6379" ) # use por 6379 for redis or 5672 for RabbitMQ -BROKER_TYPE = os.getenv("BROKER_TYPE", "amqp") # use amqp for RabbitMQ or redis +BROKER_TYPE = os.getenv("BROKER_TYPE", "redis") # use amqp for RabbitMQ or redis # Redus backend config BACKEND_TYPE = os.getenv("BACKEND_TYPE", "redis") BACKEND_USER = os.getenv("BACKEND_USER", "") diff --git a/sld-api-backend/requirements.txt b/sld-api-backend/requirements.txt index 6764324f..248c120c 100644 --- a/sld-api-backend/requirements.txt +++ b/sld-api-backend/requirements.txt @@ -1,68 +1,73 @@ aioredis==2.0.1 amqp==5.1.1 -anyio==3.6.2 -async-timeout==4.0.2 -attrs==22.2.0 +annotated-types==0.6.0 +anyio==3.7.1 +async-timeout==4.0.3 +attrs==23.1.0 bcrypt==4.0.1 -billiard==3.6.4.0 -celery==5.2.7 -certifi==2022.12.7 -cffi==1.15.1 -charset-normalizer==3.0.1 -click==8.1.3 +billiard==4.1.0 +celery==5.3.4 +certifi==2023.7.22 +cffi==1.16.0 +charset-normalizer==3.3.0 +click==8.1.7 click-didyoumean==0.3.0 click-plugins==1.1.1 -click-repl==0.2.0 -croniter==1.3.8 -cryptography==39.0.1 +click-repl==0.3.0 +croniter==2.0.1 +cryptography==41.0.4 dependency-injector==4.41.0 -Deprecated==1.2.13 -dnspython==2.3.0 +Deprecated==1.2.14 +dnspython==2.4.2 ecdsa==0.18.0 -email-validator==1.3.1 -exceptiongroup==1.1.0 -fastapi==0.91.0 +email-validator==2.0.0.post2 +exceptiongroup==1.1.3 +fastapi==0.104.1 fastapi-limiter==0.1.5 fastapi-versioning==0.10.0 -greenlet==2.0.2 +greenlet==3.0.0 h11==0.14.0 idna==3.4 iniconfig==2.0.0 Jinja2==3.1.2 jmespath==1.0.1 -kombu==5.2.4 -MarkupSafe==2.1.2 -mysqlclient==2.1.1 -packaging==23.0 +kombu==5.3.2 +MarkupSafe==2.1.3 +mysqlclient==2.2.0 +packaging==23.2 passlib==1.7.4 password-strength==0.0.3.post2 -pluggy==1.0.0 -prompt-toolkit==3.0.36 +pluggy==1.3.0 +prompt-toolkit==3.0.39 py==1.11.0 -pyasn1==0.4.8 +pyasn1==0.5.0 pycparser==2.21 -pydantic==1.10.4 -pyhcl==0.4.4 -PyJWT==2.6.0 -PyMySQL==1.0.2 -pyparsing==3.0.9 -pytest==7.2.1 +pydantic==2.4.2 +pydantic-settings==2.0.3 +pydantic_core==2.10.1 +pyhcl==0.4.5 +PyJWT==2.8.0 +PyMySQL==1.1.0 +pyparsing==3.1.1 +pytest==7.4.2 python-dateutil==2.8.2 +python-dotenv==1.0.0 python-jose==3.3.0 -python-multipart==0.0.5 -python-usernames==0.3.1 -pytz==2022.7.1 -redis==4.5.1 -requests==2.28.2 +python-multipart==0.0.6 +python-usernames==1.0.0 +pytz==2023.3.post1 +redis==4.6.0 +requests==2.31.0 rsa==4.9 six==1.16.0 sniffio==1.3.0 -SQLAlchemy==2.0.3 -starlette==0.24.0 +SQLAlchemy==2.0.22 +starlette==0.27.0 tomli==2.0.1 -typing_extensions==4.4.0 -urllib3==1.26.14 -uvicorn==0.20.0 +typing_extensions==4.8.0 +tzdata==2023.3 +urllib3==2.0.7 +uvicorn==0.23.2 vine==5.0.0 -wcwidth==0.2.6 -wrapt==1.14.1 +wcwidth==0.2.8 +wrapt==1.15.0 diff --git a/sld-api-backend/src/users/application/validator.py b/sld-api-backend/src/users/application/validator.py index 3d667a95..db94823e 100644 --- a/sld-api-backend/src/users/application/validator.py +++ b/sld-api-backend/src/users/application/validator.py @@ -2,7 +2,7 @@ from dependency_injector import containers, providers from password_strength import PasswordPolicy -from usernames import is_safe_username +from python_usernames import is_safe_username class PasswordValidator: diff --git a/sld-api-backend/src/worker/providers/hashicorp/actions.py b/sld-api-backend/src/worker/providers/hashicorp/actions.py index 8636df6c..1dc4ac94 100644 --- a/sld-api-backend/src/worker/providers/hashicorp/actions.py +++ b/sld-api-backend/src/worker/providers/hashicorp/actions.py @@ -80,7 +80,7 @@ def plan_execute(self) -> dict: "tfvars_files": self.variables_file, "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", "project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", - "stdout": [result.stderr.split("\n")], + "stdout": result.stderr.split("\n"), } return { "command": "plan", @@ -92,7 +92,7 @@ def plan_execute(self) -> dict: "tfvars_files": self.variables_file, "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", "project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", - "stdout": [result.stdout.split("\n")], + "stdout": result.stdout.split("\n"), } except Exception: return { @@ -105,7 +105,7 @@ def plan_execute(self) -> dict: "tfvars_files": self.variables_file, "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", "project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", - "stdout": [result.stderr.split("\n")], + "stdout": result.stderr.split("\n"), } def apply_execute(self) -> dict: @@ -156,7 +156,7 @@ def apply_execute(self) -> dict: "tfvars_files": self.variables_file, "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", "self.project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", - "stdout": [result.stderr.split("\n")], + "stdout": result.stderr.split("\n"), } return { "command": "apply", @@ -168,7 +168,7 @@ def apply_execute(self) -> dict: "tfvars_files": self.variables_file, "self.project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", - "stdout": [result.stdout.split("\n")], + "stdout": result.stdout.split("\n"), } except Exception: return { @@ -235,7 +235,7 @@ def destroy_execute(self) -> dict: "tfvars_files": self.variables_file, "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", "self.project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", - "stdout": [result.stderr.split("\n")], + "stdout": result.stderr.split("\n"), } return { "command": "destroy", @@ -247,7 +247,7 @@ def destroy_execute(self) -> dict: "tfvars_files": self.variables_file, "self.project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", - "stdout": [result.stdout.split("\n")], + "stdout": result.stdout.split("\n"), } except Exception: return { @@ -260,7 +260,6 @@ def destroy_execute(self) -> dict: "tfvars_files": self.variables_file, "self.project_path": f"/tmp/{self.stack_name}/{self.environment}/{self.squad}/{self.name}/{self.project_path}", "remote_state": f"http://remote-state:8080/terraform_state/{deploy_state}", - # "stdout": [result.stderr.split("\n")], "stdout": "ko", } diff --git a/sld-api-backend/src/worker/providers/hashicorp/templates.py b/sld-api-backend/src/worker/providers/hashicorp/templates.py index ca261d4a..52c59e48 100644 --- a/sld-api-backend/src/worker/providers/hashicorp/templates.py +++ b/sld-api-backend/src/worker/providers/hashicorp/templates.py @@ -41,7 +41,7 @@ def save(self) -> dict: tf_state.write(provider_backend) return {"command": "tfserver", "rc": 0, "stdout": data} except Exception as err: - return {"command": "tfserver", "rc": 1, "stderr": err} + return {"command": "tfserver", "rc": 1, "stdout": str(err)} @dataclass diff --git a/sld-api-backend/test/config/api.py b/sld-api-backend/test/config/api.py index a4c5d69a..2afa5d74 100644 --- a/sld-api-backend/test/config/api.py +++ b/sld-api-backend/test/config/api.py @@ -1,7 +1,6 @@ import os -from pydantic import BaseSettings - +from pydantic_settings import BaseSettings class Settings(BaseSettings): SERVER: str = "http://localhost" diff --git a/sld-dashboard/.tool-versions b/sld-dashboard/.tool-versions new file mode 100644 index 00000000..b4736d5d --- /dev/null +++ b/sld-dashboard/.tool-versions @@ -0,0 +1 @@ +python 3.11.6 diff --git a/sld-dashboard/Dockerfile b/sld-dashboard/Dockerfile index c308f7ed..8eecf14d 100644 --- a/sld-dashboard/Dockerfile +++ b/sld-dashboard/Dockerfile @@ -1,41 +1,54 @@ FROM ubuntu:22.04 -MAINTAINER D10S0VSkY +# Metadata +MAINTAINER D10S0VSkY ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 ENV TZ=Europe/Madrid -ENV FLASK_APP run.py +# Set up working directory WORKDIR /app ADD ./requirements.txt /app/requirements.txt +# Create a user and group RUN groupadd --gid 10000 sld && \ - useradd --uid 10000 --gid sld --shell /bin/bash --create-home sld - -COPY run.py gunicorn-cfg.py requirements.txt config.py .env ./ + useradd --uid 10000 --gid sld --shell /bin/bash --create-home sld +# Set timezone RUN echo $TZ > /etc/timezone && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime +# Install dependencies including build tools RUN export DEBIAN_FRONTEND=noninteractive && \ -apt-get update && \ -apt-get -yq install software-properties-common && \ -add-apt-repository ppa:deadsnakes/ppa + apt-get update && \ + apt-get -yq install curl git zip tzdata build-essential libssl-dev libffi-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev pkg-config libmysqlclient-dev + + +# Install asdf, Python plugin, and Python version +RUN git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.0 && \ + echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc && \ + echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc + +SHELL ["/bin/bash", "-c"] + +RUN . $HOME/.asdf/asdf.sh && \ + asdf plugin add python && \ + asdf install python 3.11.6 && \ + asdf global python 3.11.6 -RUN export DEBIAN_FRONTEND=noninteractive && \ -apt-get update && \ -apt-get upgrade -yq && \ -apt-get -yq install \ -python3.11 pip default-libmysqlclient-dev zip git tzdata python3.11-dev -RUN ln -s /usr/bin/python3.11 /usr/bin/python && \ -python -m pip install --upgrade setuptools && \ -python -m pip install --no-cache-dir -r requirements.txt +# Install Python packages +RUN . $HOME/.asdf/asdf.sh && \ + python -m pip install --upgrade pip setuptools && \ + python -m pip install --no-cache-dir -r requirements.txt +# Clean up RUN apt-get clean autoclean && \ -apt-get autoremove -y && \ -rm -rf /var/lib/{apt,dpkg,cache,log}/ + apt-get autoremove -y && \ + rm -rf /var/lib/{apt,dpkg,cache,log} +# Add the rest of the application ADD . /app/ RUN chown -R sld /app +# Switch to user USER sld diff --git a/sld-dashboard/app/__init__.py b/sld-dashboard/app/__init__.py index 1a17e638..2611d2c4 100644 --- a/sld-dashboard/app/__init__.py +++ b/sld-dashboard/app/__init__.py @@ -23,12 +23,12 @@ def register_blueprints(app): def configure_database(app): - @app.before_first_request - def initialize_database(): - try: - pass - except Exception as err: - pass + with app.app_context(): + def initialize_database(): + try: + pass + except Exception as err: + pass @app.teardown_request def shutdown_session(exception=None): diff --git a/sld-dashboard/app/base/static/assets/css/volt.css b/sld-dashboard/app/base/static/assets/css/volt.css index 739262a4..361b11b9 100644 --- a/sld-dashboard/app/base/static/assets/css/volt.css +++ b/sld-dashboard/app/base/static/assets/css/volt.css @@ -41710,3 +41710,17 @@ pre { display: block; word-wrap: break-word; } + +.plus { color: #54C571; } +.minus { color: #C34A2C; } +.tilde { color: #6698FF; } + +.modal-black-background { + background-color: black; + color: white; +} + +.textarea-right-aligned { + direction: rtl; /* Right-to-left */ + text-align: right; /* For good measure, in case of LTR content */ +} diff --git a/sld-dashboard/app/base/static/assets/js/pagination.js b/sld-dashboard/app/base/static/assets/js/pagination.js new file mode 100644 index 00000000..3e2b19ff --- /dev/null +++ b/sld-dashboard/app/base/static/assets/js/pagination.js @@ -0,0 +1,76 @@ +$(document).ready(function(){ + var rowsPerPage = 10; // Valor inicial + var rows = $('#myTable tr'); + var filteredRows = rows; // Inicialmente, todas las filas son el conjunto filtrado + var pagesCount; + var currentPage = 1; + + function displayPage(page) { + var start = (page - 1) * rowsPerPage; + var end = start + rowsPerPage; + rows.hide(); + filteredRows.slice(start, end).show(); + } + + function setupPagination() { + pagesCount = Math.ceil(filteredRows.length / rowsPerPage); + $('.pagination .number-page').remove(); // Remove old page numbers + for (var i = 1; i <= pagesCount; i++) { + $('
  • ' + i + '
  • ') + .insertBefore("#next-page") + .on('click', function(e) { + e.preventDefault(); + currentPage = parseInt($(this).text()); + displayPage(currentPage); + $(".pagination .number-page").removeClass('active'); + $(this).addClass('active'); + }); + } + displayPage(1); + } + + // Event Delegation for edit buttons and other interactive elements + $(document).on('click', '.edit-button', function() { + // Aquí iría el código para manejar la edición + }); + + // Existing search functionality + $("#myInput").on("keyup", function() { + var value = $(this).val().toLowerCase(); + filteredRows = rows.filter(function() { + return $(this).text().toLowerCase().indexOf(value) > -1; + }); + currentPage = 1; + setupPagination(); + }); + + // Existing dropdown functionality + $(".dropdown-menu a").on("click", function(e) { + e.preventDefault(); + var selectedValue = $(this).text().trim(); + rowsPerPage = selectedValue.toLowerCase() === 'all' ? rows.length : parseInt(selectedValue); + currentPage = 1; // Reset to first page + setupPagination(); + }); + + // Initial setup + setupPagination(); + + // Previous and Next button logic + $("#previous-page").on('click', function(e) { + e.preventDefault(); + if (currentPage > 1) { + currentPage--; + displayPage(currentPage); + } + }); + + $("#next-page").on('click', function(e) { + e.preventDefault(); + if (currentPage < pagesCount) { + currentPage++; + displayPage(currentPage); + } + }); +}); +d \ No newline at end of file diff --git a/sld-dashboard/app/base/static/assets/js/pagination_edit.js b/sld-dashboard/app/base/static/assets/js/pagination_edit.js new file mode 100644 index 00000000..8627070b --- /dev/null +++ b/sld-dashboard/app/base/static/assets/js/pagination_edit.js @@ -0,0 +1,85 @@ + document.addEventListener("DOMContentLoaded", function() { + var rowsPerPage = 10; + var table = document.getElementById("table"); + var tbody = table.getElementsByTagName("tbody")[0]; + var rows = Array.from(tbody.getElementsByTagName("tr")); + var pagination = document.querySelector(".pagination"); + var currentPage = 1; + + function displayPage(page, rowsToShow) { + var start = (page - 1) * rowsPerPage; + var end = start + rowsPerPage; + rows.forEach(row => row.style.display = "none"); + rowsToShow.slice(start, end).forEach(row => row.style.display = ""); + } + + function setupPagination(rowsToShow) { + pagination.innerHTML = ''; + var pagesCount = Math.ceil(rowsToShow.length / rowsPerPage); + + // Previous page button + var prevPageItem = document.createElement("li"); + prevPageItem.className = "page-item"; + var prevPageLink = document.createElement("a"); + prevPageLink.className = "page-link"; + prevPageLink.href = "#"; + prevPageLink.innerText = "Previous"; + prevPageItem.appendChild(prevPageLink); + pagination.appendChild(prevPageItem); + + // Page number buttons + for (var i = 1; i <= pagesCount; i++) { + var pageItem = document.createElement("li"); + pageItem.className = "page-item"; + var pageLink = document.createElement("a"); + pageLink.className = "page-link"; + pageLink.href = "#"; + pageLink.innerText = i; + pageItem.appendChild(pageLink); + pagination.appendChild(pageItem); + + pageLink.addEventListener("click", function(e) { + e.preventDefault(); + currentPage = parseInt(this.innerText); + displayPage(currentPage, rowsToShow); + }); + } + + // Next page button + var nextPageItem = document.createElement("li"); + nextPageItem.className = "page-item"; + var nextPageLink = document.createElement("a"); + nextPageLink.className = "page-link"; + nextPageLink.href = "#"; + nextPageLink.innerText = "Next"; + nextPageItem.appendChild(nextPageLink); + pagination.appendChild(nextPageItem); + + prevPageLink.addEventListener("click", function(e) { + e.preventDefault(); + if (currentPage > 1) { + displayPage(--currentPage, rowsToShow); + } + }); + + nextPageLink.addEventListener("click", function(e) { + e.preventDefault(); + if (currentPage < pagesCount) { + displayPage(++currentPage, rowsToShow); + } + }); + + displayPage(1, rowsToShow); // Display the first page + } + + function updateSearchAndPagination() { + var filter = document.getElementById("search").value.toUpperCase(); + var filteredRows = rows.filter(row => row.textContent.toUpperCase().indexOf(filter) > -1); + currentPage = 1; + setupPagination(filteredRows); + } + + document.getElementById("search").addEventListener("keyup", updateSearchAndPagination); + + setupPagination(rows); // Initial setup with all rows + }); \ No newline at end of file diff --git a/sld-dashboard/app/helpers/config/api.py b/sld-dashboard/app/helpers/config/api.py index 2ab9c7eb..f94538e2 100644 --- a/sld-dashboard/app/helpers/config/api.py +++ b/sld-dashboard/app/helpers/config/api.py @@ -1,6 +1,6 @@ import os -from pydantic import BaseSettings +from pydantic_settings import BaseSettings class Settings(BaseSettings): diff --git a/sld-dashboard/app/helpers/converter.py b/sld-dashboard/app/helpers/converter.py index 0b172fb4..bf162772 100644 --- a/sld-dashboard/app/helpers/converter.py +++ b/sld-dashboard/app/helpers/converter.py @@ -1,4 +1,5 @@ import ast +import json def convert_to_dict(data): @@ -17,3 +18,10 @@ def convert_to_dict(data): if not len(result): return data return result[0] + +def trim_dict(valor): + try: + obj = json.loads(valor) + return json.dumps(obj) + except json.JSONDecodeError: + return valor diff --git a/sld-dashboard/app/home/__init__.py b/sld-dashboard/app/home/__init__.py index ee3a0641..a3dcb7ec 100644 --- a/sld-dashboard/app/home/__init__.py +++ b/sld-dashboard/app/home/__init__.py @@ -58,7 +58,7 @@ def get_output(task_id, token): return {"result": content.get("result").get("status"), "type": "str"} if not isinstance(data, list): return {"result": content.get("result").get("module").get("stdout")} - return {"result": content.get("result").get("module").get("stdout")[0]} + return {"result": content.get("result").get("module").get("stdout")} except Exception as err: return {"result": response} diff --git a/sld-dashboard/app/home/routes.py b/sld-dashboard/app/home/routes.py index 5d2fae9f..34ee5a93 100644 --- a/sld-dashboard/app/home/routes.py +++ b/sld-dashboard/app/home/routes.py @@ -688,7 +688,6 @@ def deploy_stack(stack_id): "project_path": request.form.get("project_path").replace(" ",""), "variables": variables, } - print(data) endpoint = f"plan" if not "plan" in request.form.get("button"): endpoint = f"deploy" diff --git a/sld-dashboard/app/home/templates/activity-logs.html b/sld-dashboard/app/home/templates/activity-logs.html index 8acb6bf4..dd159fea 100644 --- a/sld-dashboard/app/home/templates/activity-logs.html +++ b/sld-dashboard/app/home/templates/activity-logs.html @@ -135,7 +135,14 @@

    All Activity

    - + + {% include 'includes/footer.html' %} @@ -143,14 +150,5 @@

    All Activity

    {% block javascripts %} - + {% endblock javascripts %} diff --git a/sld-dashboard/app/home/templates/aws-list.html b/sld-dashboard/app/home/templates/aws-list.html index 88d3178c..a247d2ca 100644 --- a/sld-dashboard/app/home/templates/aws-list.html +++ b/sld-dashboard/app/home/templates/aws-list.html @@ -38,7 +38,7 @@

    All aws accounts

    -
    @@ -76,7 +76,7 @@

    All aws accounts

    - + {% for aws_account in aws %} @@ -132,7 +132,14 @@

    All aws accounts

    - + + {% include 'includes/footer.html' %} @@ -141,23 +148,6 @@

    All aws accounts

    {% block javascripts %} - + {% endblock javascripts %} + diff --git a/sld-dashboard/app/home/templates/azure-list.html b/sld-dashboard/app/home/templates/azure-list.html index 5c309bdf..0dedd943 100644 --- a/sld-dashboard/app/home/templates/azure-list.html +++ b/sld-dashboard/app/home/templates/azure-list.html @@ -38,7 +38,7 @@

    All azure accounts

    -
    @@ -74,7 +74,7 @@

    All azure accounts

    - + {% for azure_account in azure %} @@ -128,7 +128,14 @@

    All azure accounts

    - + + {% include 'includes/footer.html' %} @@ -137,24 +144,5 @@

    All azure accounts

    {% block javascripts %} - -{% endblock javascripts %} + +{% endblock javascripts %} \ No newline at end of file diff --git a/sld-dashboard/app/home/templates/custom-provider-list.html b/sld-dashboard/app/home/templates/custom-provider-list.html index d6059940..cdb36f11 100644 --- a/sld-dashboard/app/home/templates/custom-provider-list.html +++ b/sld-dashboard/app/home/templates/custom-provider-list.html @@ -38,7 +38,7 @@

    All custom providers accounts

    -
    @@ -72,7 +72,7 @@

    All custom providers accounts

    - + {% for custom_provider in custom_provider_content %} @@ -124,6 +124,14 @@

    All custom providers accounts

    + + {% include 'includes/footer.html' %} @@ -133,24 +141,5 @@

    All custom providers accounts

    {% block javascripts %} - -{% endblock javascripts %} + +{% endblock javascripts %} \ No newline at end of file diff --git a/sld-dashboard/app/home/templates/deploys-list.html b/sld-dashboard/app/home/templates/deploys-list.html index d9276a17..6739eb0b 100644 --- a/sld-dashboard/app/home/templates/deploys-list.html +++ b/sld-dashboard/app/home/templates/deploys-list.html @@ -52,7 +52,7 @@

    All Deploys

    - +
    @@ -62,6 +62,7 @@

    All Deploys

    +
    @@ -110,7 +107,7 @@

    All Deploys

    - + {% for deploy in deploys| sort(attribute='id') %} @@ -155,7 +152,7 @@

    All Deploys

    Toggle Dropdown - {% if deploy.action == "Plan"%} + {% if deploy.action == "Plan_disable"%} -
    +
    @@ -119,52 +120,24 @@

    All users

    + {% include 'includes/footer.html' %} {% endblock content %} - {% block javascripts %} - + {% endblock javascripts %} diff --git a/sld-dashboard/requirements.txt b/sld-dashboard/requirements.txt index 457496ab..c9b87de5 100644 --- a/sld-dashboard/requirements.txt +++ b/sld-dashboard/requirements.txt @@ -1,45 +1,50 @@ -alembic==1.8.1 -async-timeout==4.0.2 +alembic==1.12.0 +annotated-types==0.6.0 +async-timeout==4.0.3 bcrypt==4.0.1 -certifi==2022.12.7 -cffi==1.15.1 -chardet==5.1.0 -charset-normalizer==2.1.1 -click==8.1.3 -cryptography==39.0.1 -Deprecated==1.2.13 -dnspython==2.2.1 -email-validator==1.3.0 +blinker==1.6.3 +certifi==2023.7.22 +cffi==1.16.0 +chardet==5.2.0 +charset-normalizer==3.3.0 +click==8.1.7 +cryptography==41.0.4 +Deprecated==1.2.14 +dnspython==2.4.2 +email-validator==2.0.0.post2 fernet==1.0.1 -Flask==2.2.2 +Flask==2.3.3 Flask-Login==0.6.2 -Flask-Migrate==4.0.0 -Flask-MySQLdb==1.0.1 -Flask-SQLAlchemy==3.0.2 -Flask-WTF==1.0.1 -greenlet==2.0.1 -gunicorn==20.1.0 +Flask-Migrate==4.0.5 +Flask-MySQLdb==2.0.0 +Flask-SQLAlchemy==3.1.1 +Flask-WTF==1.2.1 +greenlet==3.0.0 +gunicorn==21.2.0 idna==3.4 itsdangerous==2.1.2 Jinja2==3.1.2 Mako==1.2.4 -MarkupSafe==2.1.1 -mysqlclient==2.1.1 -packaging==21.3 +MarkupSafe==2.1.3 +mysqlclient==2.2.0 +packaging==23.2 passlib==1.7.4 pyaes==1.6.1 pycparser==2.21 -pydantic==1.10.2 -pyparsing==3.0.9 +pydantic==2.4.2 +pydantic-settings==2.0.3 +pydantic_core==2.10.1 +pyparsing==3.1.1 python-dateutil==2.8.2 -python-decouple==3.6 +python-decouple==3.8 +python-dotenv==1.0.0 python-editor==1.0.4 -redis==4.3.5 -requests==2.28.1 +redis==5.0.1 +requests==2.31.0 six==1.16.0 -SQLAlchemy==1.4.44 -typing_extensions==4.4.0 -urllib3==1.26.13 -Werkzeug==2.2.2 -wrapt==1.14.1 -WTForms==3.0.1 +SQLAlchemy==2.0.22 +typing_extensions==4.8.0 +urllib3==2.0.7 +Werkzeug==2.3.7 +wrapt==1.15.0 +WTForms==3.1.0 diff --git a/sld-remote-state/.tool-versions b/sld-remote-state/.tool-versions new file mode 100644 index 00000000..b4736d5d --- /dev/null +++ b/sld-remote-state/.tool-versions @@ -0,0 +1 @@ +python 3.11.6 diff --git a/sld-remote-state/Dockerfile b/sld-remote-state/Dockerfile index ee6e28c4..2ff089a4 100644 --- a/sld-remote-state/Dockerfile +++ b/sld-remote-state/Dockerfile @@ -1,38 +1,53 @@ FROM ubuntu:22.04 -MAINTAINER D10S0VSkY +# Metadata +MAINTAINER D10S0VSkY ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 ENV TZ=Europe/Madrid +# Set up working directory WORKDIR /app ADD ./requirements.txt /app/requirements.txt +# Create a user and group RUN groupadd --gid 10000 sld && \ - useradd --uid 10000 --gid sld --shell /bin/bash --create-home sld + useradd --uid 10000 --gid sld --shell /bin/bash --create-home sld +# Set timezone RUN echo $TZ > /etc/timezone && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime +# Install dependencies including build tools RUN export DEBIAN_FRONTEND=noninteractive && \ -apt-get update && \ -apt-get -yq install software-properties-common && \ -add-apt-repository ppa:deadsnakes/ppa + apt-get update && \ + apt-get -yq install curl git zip tzdata build-essential libssl-dev libffi-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev + +# Install asdf, Python plugin, and Python version +RUN git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.10.0 && \ + echo '. $HOME/.asdf/asdf.sh' >> ~/.bashrc && \ + echo '. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc + +SHELL ["/bin/bash", "-c"] + +RUN . $HOME/.asdf/asdf.sh && \ + asdf plugin add python && \ + asdf install python 3.11.6 && \ + asdf global python 3.11.6 -RUN export DEBIAN_FRONTEND=noninteractive && \ -apt-get update && \ -apt-get upgrade -yq && \ -apt-get -yq install \ -python3.11 pip default-libmysqlclient-dev zip git tzdata python3.11-dev -RUN ln -s /usr/bin/python3.11 /usr/bin/python && \ -python -m pip install --upgrade setuptools && \ -python -m pip install --no-cache-dir -r requirements.txt +# Install Python packages +RUN . $HOME/.asdf/asdf.sh && \ + python -m pip install --upgrade pip setuptools && \ + python -m pip install --no-cache-dir -r requirements.txt +# Clean up RUN apt-get clean autoclean && \ -apt-get autoremove -y && \ -rm -rf /var/lib/{apt,dpkg,cache,log} + apt-get autoremove -y && \ + rm -rf /var/lib/{apt,dpkg,cache,log} +# Add the rest of the application ADD . /app/ RUN chown -R sld /app +# Switch to user USER sld diff --git a/sld-remote-state/configs/storage.py b/sld-remote-state/configs/storage.py index 0b433d7c..0a63c598 100644 --- a/sld-remote-state/configs/storage.py +++ b/sld-remote-state/configs/storage.py @@ -1,6 +1,6 @@ import os -from pydantic import BaseSettings +from pydantic_settings import BaseSettings class Settings(BaseSettings): diff --git a/sld-remote-state/requirements.txt b/sld-remote-state/requirements.txt index b7ddb4da..91015fb9 100644 --- a/sld-remote-state/requirements.txt +++ b/sld-remote-state/requirements.txt @@ -1,46 +1,50 @@ -anyio==3.6.2 -asgiref==3.5.2 -azure-core==1.26.1 -azure-storage-blob==12.14.1 -boto3==1.26.21 -botocore==1.29.21 -cachetools==5.2.0 -certifi==2022.12.7 -cffi==1.15.1 -charset-normalizer==2.1.1 -click==8.1.3 -cryptography==39.0.1 -dnspython==2.2.1 -fastapi==0.88.0 -google-api-core==2.11.0 -google-auth==2.15.0 -google-cloud-core==2.3.2 -google-cloud-storage==2.6.0 +annotated-types==0.6.0 +anyio==3.7.1 +asgiref==3.7.2 +azure-core==1.29.5 +azure-storage-blob==12.18.3 +boto3==1.28.79 +botocore==1.31.79 +cachetools==5.3.2 +certifi==2023.7.22 +cffi==1.16.0 +charset-normalizer==3.3.2 +click==8.1.7 +cryptography==41.0.5 +dnspython==2.4.2 +fastapi==0.104.1 +google-api-core==2.12.0 +google-auth==2.23.4 +google-cloud-core==2.3.3 +google-cloud-storage==2.13.0 google-crc32c==1.5.0 -google-resumable-media==2.4.0 -googleapis-common-protos==1.57.0 -greenlet==2.0.1 +google-resumable-media==2.6.0 +googleapis-common-protos==1.61.0 +greenlet==3.0.1 h11==0.14.0 idna==3.4 isodate==0.6.1 jmespath==1.0.1 msrest==0.7.1 oauthlib==3.2.2 -protobuf==4.21.10 -pyasn1==0.4.8 -pyasn1-modules==0.2.8 +protobuf==4.25.0 +pyasn1==0.5.0 +pyasn1-modules==0.3.0 pycparser==2.21 -pydantic==1.10.2 -pymongo==4.3.3 +pydantic==2.4.2 +pydantic-settings==2.0.3 +pydantic_core==2.10.1 +pymongo==4.6.0 python-dateutil==2.8.2 -requests==2.28.1 +python-dotenv==1.0.0 +requests==2.31.0 requests-oauthlib==1.3.1 rsa==4.9 -s3transfer==0.6.0 +s3transfer==0.7.0 six==1.16.0 sniffio==1.3.0 -SQLAlchemy==1.4.44 -starlette==0.22.0 -typing_extensions==4.4.0 -urllib3==1.26.13 -uvicorn==0.20.0 +SQLAlchemy==2.0.23 +starlette==0.27.0 +typing_extensions==4.8.0 +urllib3==2.0.7 +uvicorn==0.24.0.post1