Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
556de27
ecs setup init
deepakdinesh1123 Aug 5, 2025
85c2e30
update task definition and method of fetching secrets
deepakdinesh1123 Aug 6, 2025
264fde8
update nginx config
deepakdinesh1123 Aug 6, 2025
08300c3
add fetch-secrets service
deepakdinesh1123 Aug 6, 2025
efc2bb9
update task definition
deepakdinesh1123 Aug 6, 2025
342899d
copy secrets
deepakdinesh1123 Aug 6, 2025
4ac677a
ecs secrets fix
deepakdinesh1123 Aug 7, 2025
d49a537
read storage class from .env
deepakdinesh1123 Aug 7, 2025
238f1b4
change ecs network mode
deepakdinesh1123 Aug 7, 2025
ddd51a6
run sync_static and collecstatic when workspace is checked out
deepakdinesh1123 Aug 8, 2025
c2c895d
update version check logic in update_apps
deepakdinesh1123 Aug 8, 2025
33054cf
dockerfiles update
deepakdinesh1123 Aug 11, 2025
09a90fb
settings changes
deepakdinesh1123 Aug 11, 2025
20728fc
dockerfile fix
deepakdinesh1123 Aug 11, 2025
56d084c
requirements fix
deepakdinesh1123 Aug 11, 2025
f10aa64
change setting
deepakdinesh1123 Aug 11, 2025
4c37d84
settings change
deepakdinesh1123 Aug 11, 2025
e920a33
run sync_static and collectstatic only for local storage
deepakdinesh1123 Aug 12, 2025
a05adaf
sync static and collecstatic
deepakdinesh1123 Aug 12, 2025
1073581
otel fix
deepakdinesh1123 Aug 13, 2025
af891df
remove tf templates
deepakdinesh1123 Aug 14, 2025
98e9992
remove fetch-secrets.sh
deepakdinesh1123 Aug 14, 2025
22c65cb
remove secrets volume
deepakdinesh1123 Aug 14, 2025
3fd229a
add gunicorn request line limit
deepakdinesh1123 Aug 14, 2025
fbe50bb
revert gunicorn change
deepakdinesh1123 Aug 14, 2025
74f33a1
source env variables correctly
deepakdinesh1123 Aug 14, 2025
f65fbcf
push project base image and rename dockerfile
deepakdinesh1123 Aug 18, 2025
d259b44
defer pushing project base image
deepakdinesh1123 Aug 18, 2025
a33eb14
dockerfile changes
deepakdinesh1123 Aug 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ frontend/
LICENSE
MANIFEST.in
README.md
__pycache__/
backend/src/zango.egg-info/
Zango/build/
22 changes: 20 additions & 2 deletions .github/workflows/release-zango.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,39 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and Push Docker image
- name: Build and Push Zango Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ secrets.DOCKERHUB_USERNAME }}/zango:latest

- name: Build and Push Docker image with tag
- name: Build and Push Zango Docker image with tag
uses: docker/build-push-action@v4
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ secrets.DOCKERHUB_USERNAME }}/zango:${{ needs.check-publish.outputs.version }}

# - name: Build and Push Zango Project Docker image
# uses: docker/build-push-action@v4
# with:
# context: .
# file: ./deploy/project.base.dockerfile
# push: true
# platforms: linux/amd64,linux/arm64
# tags: ${{ secrets.DOCKERHUB_USERNAME }}/zango-base-project:latest

# - name: Build and Push Zango Project Docker image with tag
# uses: docker/build-push-action@v4
# with:
# context: .
# file: ./deploy/project.base.dockerfile
# push: true
# platforms: linux/amd64,linux/arm64
# tags: ${{ secrets.DOCKERHUB_USERNAME }}/zango-base-project:${{ needs.check-publish.outputs.version }}

build-zango:
name: Build zango
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,13 @@ package-lock.json
**/plugins/

.docusaurus

.terraform/
*.tfstate
*.tfstate.*

# Local tfvars terraform.tfvars
**/*.tfvars

# tf lock file
**/.terraform.lock.hcl
44 changes: 41 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
FROM python:3.10
RUN apt-get update && apt-get install -y libxml2-dev libxmlsec1-dev libxmlsec1-openssl
FROM python:3.10-bookworm
RUN apt-get update && apt-get install -y libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \
net-tools \
ffmpeg \
software-properties-common \
fontconfig \
libxrender1 \
libxext6 \
wget \
xfonts-base \
xfonts-75dpi \
fonts-lato \
fonts-noto-core \
fonts-roboto \
fonts-open-sans \
cabextract xfonts-utils && \
mkdir -p /usr/share/fonts/truetype/msttcore && \
cd /usr/share/fonts/truetype/msttcore && \
wget https://downloads.sourceforge.net/corefonts/arial32.exe && \
cabextract -F '*.ttf' arial32.exe && \
fc-cache -fv && \
rm arial32.exe \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

RUN wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb && \
dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb && \
rm libssl1.1_1.1.0g-2ubuntu4_amd64.deb

# Download and install wkhtmltopdf from an alternative source
RUN wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.buster_amd64.deb && \
dpkg -i wkhtmltox_0.12.6-1.buster_amd64.deb && \
apt-get install -f -y && \
rm wkhtmltox_0.12.6-1.buster_amd64.deb


WORKDIR /zango
# ref: https://docs.docker.com/build/cache/
RUN pip install --upgrade 'sentry-sdk[django]'
COPY backend/requirements/base.txt /backend/requirements/base.txt
RUN pip install -r /backend/requirements/base.txt
COPY . /zango/
COPY backend /zango/backend
RUN cd backend && pip install .
RUN rm -rf /zango/backend
14 changes: 13 additions & 1 deletion backend/requirements/base.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
beautifulsoup4==4.12.3
boto3==1.28.52
botocore==1.31.52
botocore==1.31.85
celery==5.3.1
click==8.1.7
cookiecutter==2.3.0
coverage==7.6.1
crispy-bootstrap5==0.7
cryptography==44.0.3
cryptography==44.0.3
django==4.2.15
django-axes==6.4.0
django-cachalot==2.6.1
Expand All @@ -31,8 +32,13 @@ djangorestframework==3.15.2
flake8==6.1.0
flower==2.0.1
GitPython==3.1.43
gunicorn==23.0.0
jsonschema==4.24.0
line-bot-sdk==3.17.1
loguru==0.7.2
lxml==5.3.0
openai==1.97.1
openpyxl==3.1.5
openpyxl==3.1.5
opentelemetry-api==1.22.0
opentelemetry-distro==0.43b0
Expand Down Expand Up @@ -64,15 +70,21 @@ opentelemetry-sdk==1.22.0
opentelemetry-semantic-conventions==0.43b0
opentelemetry-util-http==0.43b0
packaging==24.1
pandas==2.3.0
pdfkit==1.0.0
phonenumberslite==8.13.23
pluginbase==1.0.1
pre-commit==3.8.0
psycopg2-binary==2.9.9
pycryptodome==3.23.0
pydub==0.25.1
PyJWT==2.8.0
PyMuPDF==1.26.3
PyPDF2==3.0.1
python3-saml==1.16.0
pytz==2024.1
setuptools==70.1.1
single-beat==0.6.3
sqlalchemy==2.0.41
XlsxWriter==3.1.9
xmlsec==1.3.14
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,5 @@ def __setitem__(self, key, value):
SECRET_KEY = "{{secret_key}}" # Shift this to .env


# To change the media storage to S3 you can use the BACKEND class provided by the default storage
# To change the static storage to S3 you can use the BACKEND class provided by the staticfiles storage
# STORAGES = {
# "default": {"BACKEND": "zango.core.storage_utils.S3MediaStorage"},
# "staticfiles": {"BACKEND": "zango.core.storage_utils.S3StaticStorage"},
# }


# INTERNAL_IPS can contain a list of IP addresses or CIDR blocks that are considered internal.
# Both individual IP addresses and CIDR notation (e.g., '192.168.1.1' or '192.168.1.0/24') can be provided.
146 changes: 106 additions & 40 deletions backend/src/zango/cli/start_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ def create_project(

project_root = get_project_root(project_name, directory=directory)

if not directory and os.path.exists(project_root):
return False, f"Folder already exists: {project_root}"
if os.path.exists(project_root):
click.echo(
f"Project directory already exists: {project_root}. Skipping project creation."
)
return True, "Project directory already exists"

project_template_path = os.path.join(
os.path.dirname(zango.cli.__file__), "project_template"
Expand Down Expand Up @@ -130,6 +133,8 @@ def create_project(
def create_public_tenant(platform_domain_url="localhost"):
from zango.apps.shared.tenancy.models import Domain, TenantModel

created = False

# Creating public tenant
if not TenantModel.objects.filter(schema_name="public").exists():
public_tenant = TenantModel.objects.create(
Expand All @@ -143,11 +148,23 @@ def create_public_tenant(platform_domain_url="localhost"):
Domain.objects.create(
tenant=public_tenant, domain=platform_domain_url, is_primary=True
)
created = True
click.echo("Public tenant created successfully.")
else:
click.echo("Public tenant already exists. Skipping creation.")
return created


def create_platform_user(platform_username, platform_username_password):
from zango.apps.shared.platformauth.models import PlatformUserModel

# Check if platform user already exists
if PlatformUserModel.objects.filter(email=platform_username).exists():
return {
"success": True,
"message": f"Platform user with email '{platform_username}' already exists. Skipping user creation.",
}

# Creating default SuperAdmin User
result = PlatformUserModel.create_user(
name="Default Super Admin",
Expand All @@ -164,13 +181,13 @@ def create_platform_user(platform_username, platform_username_password):
@click.command(name="start-project")
@click.argument("project_name")
@click.option("--directory", help="Project Directory")
@click.option("--db_name", prompt=True, help="DB Name")
@click.option("--db_user", prompt=True, help="DB User")
@click.option("--db_password", prompt=True, hide_input=True, help="DB Password")
@click.option("--db_host", prompt=True, help="DB Host", default="127.0.0.1")
@click.option("--db_port", prompt=True, help="DB Port", default="5432")
@click.option("--redis_host", prompt=True, help="Redis Host", default="127.0.0.1")
@click.option("--redis_port", prompt=True, help="Redis Port", default="6379")
@click.option("--db_name", help="DB Name")
@click.option("--db_user", help="DB User")
@click.option("--db_password", hide_input=True, help="DB Password")
@click.option("--db_host", help="DB Host", default="127.0.0.1")
@click.option("--db_port", help="DB Port", default="5432")
@click.option("--redis_host", help="Redis Host", default="127.0.0.1")
@click.option("--redis_port", help="Redis Port", default="6379")
@click.option("--platform_username", prompt=False, help="Platform Username")
@click.option(
"--platform_domain_url",
Expand All @@ -184,6 +201,11 @@ def create_platform_user(platform_username, platform_username_password):
hide_input=True,
help="Platform User Password",
)
@click.option(
"--skip-db-setup",
is_flag=True,
help="Skip database operations, tenant and user creation (useful for base image creation)",
)
def start_project(
project_name,
directory,
Expand All @@ -197,18 +219,54 @@ def start_project(
platform_username,
platform_user_password,
platform_domain_url,
skip_db_setup,
):
"""Create Project"""
if directory:
click.echo(f"Creating Project under: {directory}")
directory = os.path.join(directory, project_name)

db_connection_status = test_db_conection(
db_name, db_user, db_password, db_host, db_port
)
click.echo(f"db_connection_status: {db_connection_status}")
if not db_connection_status:
raise click.ClickException("DB Connection Failed!")
# Handle prompting when not skipping DB setup
if not skip_db_setup:
if not db_name:
db_name = click.prompt("DB Name")
if not db_user:
db_user = click.prompt("DB User")
if not db_password:
db_password = click.prompt("DB Password", hide_input=True)
if not db_host:
db_host = click.prompt("DB Host", default="127.0.0.1")
if not db_port:
db_port = click.prompt("DB Port", default="5432")
if not redis_host:
redis_host = click.prompt("Redis Host", default="127.0.0.1")
if not redis_port:
redis_port = click.prompt("Redis Port", default="6379")
else:
# Use environment variables when skipping DB setup
if not db_name:
db_name = "POSTGRES_DB"
if not db_user:
db_user = "POSTGRES_USER"
if not db_password:
db_password = ""
if not db_host:
db_host = "POSTGRES_HOST"
if not db_port:
db_port = "POSTGRES_PORT"
if not redis_host:
redis_host = "REDIS_HOST"
if not redis_port:
redis_port = "REDIS_PORT"

# Skip DB connection test when skip_db_setup is True
if not skip_db_setup:
db_connection_status = test_db_conection(
db_name, db_user, db_password, db_host, db_port
)
click.echo(f"db_connection_status: {db_connection_status}")
if not db_connection_status:
raise click.ClickException("DB Connection Failed!")

project_status, project_message = create_project(
project_name,
Expand All @@ -224,6 +282,11 @@ def start_project(
if not project_status:
raise click.ClickException(project_message)

# Skip all database operations when skip_db_setup is True
if skip_db_setup:
click.echo("Project structure created successfully. Skipping database setup.")
return

# Initializing the project
project_root = get_project_root(project_name, directory=directory)
sys.path.insert(0, project_root)
Expand All @@ -235,29 +298,32 @@ def start_project(
call_command("migrate_schemas", schema="public")

# Creating Public Tenant
create_public_tenant(platform_domain_url=platform_domain_url)

# Prompting default platform user details
while True:
if not platform_username:
click.echo("Please enter platform user email")
platform_username = click.prompt("Email")
if not platform_user_password:
platform_user_password = click.prompt(
"Password", hide_input=True, confirmation_prompt=True
created = create_public_tenant(platform_domain_url=platform_domain_url)

if created:
# Prompting default platform user details
while True:
if not platform_username:
click.echo("Please enter platform user email")
platform_username = click.prompt("Email")
if not platform_user_password:
platform_user_password = click.prompt(
"Password", hide_input=True, confirmation_prompt=True
)

user_creation_result = create_platform_user(
platform_username, platform_user_password
)

user_creation_result = create_platform_user(
platform_username, platform_user_password
)
if user_creation_result["success"]:
break
else:
platform_username = None
platform_user_password = None
click.echo(user_creation_result["message"])
retry = click.prompt("Do you want to try again? (yes/no)", default="yes")
if retry.lower() != "yes":
raise click.ClickException("User creation aborted by the user.")

click.echo(user_creation_result["message"])
if user_creation_result["success"]:
break
else:
platform_username = None
platform_user_password = None
click.echo(user_creation_result["message"])
retry = click.prompt(
"Do you want to try again? (yes/no)", default="yes"
)
if retry.lower() != "yes":
raise click.ClickException("User creation aborted by the user.")

click.echo(user_creation_result["message"])
Loading
Loading