Skip to content
Closed
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
2c992a8
wip: implement landing page and sidebar UI
Venkateshvenki404224 Dec 7, 2025
37a5298
feat: implement Penguin UI sidebar and fix width issue (#19)
Venkateshvenki404224 Dec 25, 2025
c4d3b9f
fix: update toast colors and replace remove button with icon (#23)
Venkateshvenki404224 Dec 25, 2025
879ebaa
fix: the steps should have less z index than navbar
Venkateshvenki404224 Dec 26, 2025
60a8fc4
feat: add custom app section, in the custom app dialog show tabs if t…
Venkateshvenki404224 Dec 26, 2025
d46f5dc
fix(ui): add separate sections for Frappe Apps and Custom Apps
Venkateshvenki404224 Dec 26, 2025
2eec691
fix(ci): update Python version to 3.12
Venkateshvenki404224 Dec 26, 2025
1b7f485
fix(ci): update Python version to 3.14
Venkateshvenki404224 Dec 26, 2025
72d7f48
fix(ci): update node version to 24
Venkateshvenki404224 Dec 26, 2025
3b3be77
fix(ui): redesign the summery page
Venkateshvenki404224 Dec 26, 2025
b9720ab
fix: custom image build non type error
Venkateshvenki404224 Dec 26, 2025
4fe34a4
chore: removed the schedular to ping server
Venkateshvenki404224 Dec 26, 2025
f62e96b
feat: add DNS record instruction like frappe cloud
Venkateshvenki404224 Dec 26, 2025
07b5bc6
feat: improve domain UI and add custom apps management
Venkateshvenki404224 Dec 26, 2025
5e5c5da
feat: populate old server list when its already available.
Venkateshvenki404224 Dec 27, 2025
6d9c69d
feat(ux): add frappe icon in list apps and add app version for mainta…
Venkateshvenki404224 Dec 27, 2025
48fc193
fix: linter
Venkateshvenki404224 Dec 27, 2025
6595909
chore: rename the placeholder
Venkateshvenki404224 Dec 27, 2025
6ee1099
wip(custom_image): Working on Custom Image
Venkateshvenki404224 Dec 27, 2025
752ac37
feat: improve domain setup and apps management UI
Venkateshvenki404224 Jan 4, 2026
7249743
refactor: add translation support to error messages
Venkateshvenki404224 Jan 4, 2026
f048dd1
fix(security): secure GitHub token storage and improve custom apps ma…
Venkateshvenki404224 Jan 4, 2026
fe17d3c
fix(custom_image): improve validation, sanitization and performance
Venkateshvenki404224 Jan 4, 2026
c66e8f4
fix: improve custom image build and secure custom app
Venkateshvenki404224 Jan 4, 2026
6197375
wip: add URL state management for deployment resume
Venkateshvenki404224 Jan 6, 2026
02b7e5c
feat(deployment): sync custom image build status to site for seamless…
Venkateshvenki404224 Jan 7, 2026
2d5ef1f
refactor(ui): improve server details layout and styling
Venkateshvenki404224 Jan 7, 2026
bb91851
refactor: fix memory leaks, redundant operations, and improve validation
Venkateshvenki404224 Jan 7, 2026
c1ad4e8
chore: add app version fixtures
Venkateshvenki404224 Jan 13, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
python-version: '3.14'

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 24
check-latest: true

- name: Cache pip
Expand Down
75 changes: 75 additions & 0 deletions nano_press/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,78 @@ def check_domain_resolves_to_ip(domain: str, expected_ip: str) -> dict:
except Exception as e:
frappe.log_error(f"Error occurred while checking domain {domain}: {e}")
return {"success": False, "resolved_ips": [], "message": f"Error: {e}"}


@frappe.whitelist()
def register_custom_app(
app_name: str, github_url: str, branch: str, token: str | None = None, order: int | None = None
) -> dict:
try:
if not app_name or not github_url or not branch:
frappe.throw(_("app_name, github_url, and branch are required"))

app_name_normalized = app_name.lower().strip()

if frappe.db.exists("Apps", app_name_normalized):
app_doc = frappe.get_doc("Apps", app_name_normalized)
app_doc.repo_url = github_url
app_doc.branch = branch
if token:
app_doc.pat_token = token
if order is not None:
app_doc.order = order
app_doc.save(ignore_permissions=True)
else:
app_doc = frappe.get_doc(
{
"doctype": "Apps",
"app_name": app_name_normalized,
"repo_url": github_url,
"branch": branch,
"pat_token": token or "",
"is_custom": 1,
"enabled": 1,
"order": order if order is not None else 999,
}
)
app_doc.insert(ignore_permissions=True)

frappe.db.commit() # nosemgrep: frappe-semgrep-rules.rules.frappe-manual-commit

return {
"success": True,
"app_reference_id": app_doc.name,
"message": _("Custom app registered securely"),
}

except Exception as e:
frappe.log_error(f"Error registering custom app: {e}")
frappe.db.rollback()
return {"success": False, "message": str(e)}


@frappe.whitelist()
def delete_custom_app(app_name: str) -> dict:
try:
if not app_name:
frappe.throw(_("app_name is required"))

app_name_normalized = app_name.lower().strip()

if not frappe.db.exists("Apps", app_name_normalized):
return {"success": True, "message": _("App not found or already deleted")}

app_doc = frappe.get_doc("Apps", app_name_normalized)

if not app_doc.is_custom:
frappe.throw(_("Cannot delete non-custom apps"))

frappe.delete_doc("Apps", app_name_normalized, ignore_permissions=True)
frappe.db.commit() # nosemgrep: frappe-semgrep-rules.rules.frappe-manual-commit

return {"success": True, "message": _("Custom app deleted successfully")}

except Exception as e:
frappe.log_error(f"Error deleting custom app: {e}")
frappe.db.rollback()
return {"success": False, "message": str(e)}
26 changes: 26 additions & 0 deletions nano_press/fixtures/app_version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"docstatus": 0,
"doctype": "App Version",
"modified": "2025-12-27 17:08:23.781012",
"name": "version-14",
"scrubbed_version": "version-14",
"version": "Version 14"
},
{
"docstatus": 0,
"doctype": "App Version",
"modified": "2025-12-27 17:08:23.781478",
"name": "nightly",
"scrubbed_version": "nightly",
"version": "Nightly"
},
{
"docstatus": 0,
"doctype": "App Version",
"modified": "2025-12-27 17:08:23.780453",
"name": "version-15",
"scrubbed_version": "version-15",
"version": "Version 15"
}
]
17 changes: 9 additions & 8 deletions nano_press/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"dt": "Custom DocPerm",
"filters": {"parent": ("in", ("Frappe Site", "Server", "Apps", "Custom Image", "Ansible Log"))},
},
{"dt": "App Version"},
]

# Includes in <head>
Expand Down Expand Up @@ -68,7 +69,7 @@
# ----------

# application home page (will override Website Settings)
# home_page = "login"
home_page = "index"

# website user home page (by Role)
# role_home_page = {
Expand Down Expand Up @@ -151,13 +152,13 @@
# Scheduled Tasks
# ---------------

scheduler_events = {
"cron": {
"*/5 * * * *": [
"nano_press.utils.ansible_runner.ping_server",
],
}
}
# scheduler_events = {
# "cron": {
# "*/5 * * * *": [
# "nano_press.utils.ansible_runner.ping_server",
# ],
# }
# }
Comment on lines +155 to +161
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we removing this?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the ping server function expects ip address or server doc so I didn't have any idea on how to pass argument from hooks.py



# Testing
Expand Down
Empty file.
48 changes: 48 additions & 0 deletions nano_press/nano_press/doctype/app_version/app_version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"actions": [],
"autoname": "",
"creation": "2025-12-27 00:00:00",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": ["version", "scrubbed_version"],
"fields": [
{
"fieldname": "version",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Version",
"reqd": 1,
"unique": 1
},
{
"fieldname": "scrubbed_version",
"fieldtype": "Data",
"hidden": 1,
"label": "Scrubbed Version"
}
],
"links": [],
"modified": "2025-12-27 16:34:33.317253",
"modified_by": "Administrator",
"module": "Nano Press",
"name": "App Version",
"naming_rule": "By script",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"read": 1,
"role": "System Manager",
"write": 1
},
{
"read": 1,
"role": "Nano Press User"
}
],
"row_format": "Dynamic",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
11 changes: 11 additions & 0 deletions nano_press/nano_press/doctype/app_version/app_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright (c) 2025, Build With Hussain and contributors
# For license information, please see license.txt

from frappe.model.document import Document


class AppVersion(Document):
def autoname(self):
if self.version:
self.scrubbed_version = self.version.lower().replace(" ", "-")
self.name = self.scrubbed_version
29 changes: 24 additions & 5 deletions nano_press/nano_press/doctype/apps/apps.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
"engine": "InnoDB",
"field_order": [
"app_name",
"app_logo",
"scrubbed_name",
"repo_url",
"repository_owner",
"column_break_jjcw",
"branch",
"order",
"pat_token",
"column_break_eisq",
"is_custom",
"is_public",
"frappe",
"enabled"
Expand Down Expand Up @@ -51,11 +54,11 @@
"non_negative": 1
},
{
"depends_on": "eval: doc.is_public == 0",
"depends_on": "eval: doc.is_custom == 1",
"description": "Required for private repositories. If provided, the app will be marked as private.",
"fieldname": "pat_token",
"fieldtype": "Data",
"label": "PAT Token",
"mandatory_depends_on": "eval: doc.is_private == 1"
"fieldtype": "Password",
"label": "PAT Token"
},
{
"fieldname": "scrubbed_name",
Expand Down Expand Up @@ -86,12 +89,28 @@
"fieldname": "frappe",
"fieldtype": "Check",
"label": "Frappe"
},
{
"fieldname": "column_break_eisq",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "is_custom",
"fieldtype": "Check",
"label": "Custom"
},
{
"fieldname": "app_logo",
"fieldtype": "Attach Image",
"label": "App Logo"
}
],
"grid_page_length": 50,
"image_field": "app_logo",
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-10-24 19:24:48.880196",
"modified": "2025-12-26 22:09:46.554687",
"modified_by": "Administrator",
"module": "Nano Press",
"name": "Apps",
Expand Down
31 changes: 30 additions & 1 deletion nano_press/nano_press/doctype/apps/apps.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
# Copyright (c) 2025, Venkatesh M and contributors
# For license information, please see license.txt

# import frappe
import frappe
from frappe.model.document import Document


class Apps(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from frappe.types import DF

app_name: DF.Data
branch: DF.Data
enabled: DF.Check
frappe: DF.Check
is_custom: DF.Check
is_public: DF.Check
order: DF.Int | None
pat_token: DF.Password | None
repo_url: DF.Data
repository_owner: DF.Data | None
scrubbed_name: DF.Data | None

# end: auto-generated types

def before_insert(self):
self.scrubbed_name = self.app_name.replace(" ", "_").lower()

def validate(self):
if self.is_custom:
if self.pat_token:
self.is_public = 0
else:
self.is_public = 1
6 changes: 3 additions & 3 deletions nano_press/nano_press/doctype/custom_image/custom_image.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
{
"default": "version-15",
"fieldname": "frappe_version",
"fieldtype": "Select",
"fieldtype": "Link",
"label": "Frappe Version",
"options": "version-15\nversion-14"
"options": "App Version"
},
{
"fieldname": "column_break_zpqr",
Expand Down Expand Up @@ -104,7 +104,7 @@
"grid_page_length": 50,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2025-10-23 23:50:38.572587",
"modified": "2025-12-27 17:02:53.063389",
"modified_by": "Administrator",
"module": "Nano Press",
"name": "Custom Image",
Expand Down
Loading