diff --git a/backend/app/handlers/site.go b/backend/app/handlers/site.go index 1472381..b4fa9a0 100644 --- a/backend/app/handlers/site.go +++ b/backend/app/handlers/site.go @@ -110,6 +110,9 @@ func validateIncomingSite(site *models.Site) error { if site.NotFoundFile == "" { site.NotFoundFile = "404.html" } + if site.IndexFile == "" { + site.IndexFile = "index.html" + } if site.ForwardRules == nil { site.ForwardRules = []models.ForwardRule{} } diff --git a/backend/app/handlers/static.go b/backend/app/handlers/static.go index b599db5..96b8a75 100644 --- a/backend/app/handlers/static.go +++ b/backend/app/handlers/static.go @@ -62,7 +62,7 @@ func NewStaticHandler(storagePath string, siteRepo repository.SiteRepository) fi } if urlPath == "/" || urlPath == "." { - urlPath = "/index.html" + urlPath = "/" + site.IndexFile } basePath := filepath.Join(storagePath, site.ID) @@ -71,7 +71,7 @@ func NewStaticHandler(storagePath string, siteRepo repository.SiteRepository) fi info, err := os.Stat(filePath) if err == nil { if info.IsDir() { - indexPath := filepath.Join(filePath, "index.html") + indexPath := filepath.Join(filePath, site.IndexFile) if _, err := os.Stat(indexPath); err == nil { return c.SendFile(indexPath) } @@ -81,7 +81,7 @@ func NewStaticHandler(storagePath string, siteRepo repository.SiteRepository) fi } if site.Spa { - indexPath := filepath.Join(basePath, "index.html") + indexPath := filepath.Join(basePath, site.IndexFile) if _, err := os.Stat(indexPath); err == nil { return c.SendFile(indexPath) } diff --git a/backend/app/models/site.go b/backend/app/models/site.go index aa27893..a807cea 100644 --- a/backend/app/models/site.go +++ b/backend/app/models/site.go @@ -33,6 +33,9 @@ type Site struct { Enabled bool `json:"enabled"` Spa bool `json:"spa"` NotFoundFile string `json:"not_found_file"` + IndexFile string `json:"index_file"` + TrailingSlash *bool `json:"trailing_slash"` + CreatedAt string `json:"created_at"` ForwardRules []ForwardRule `json:"forward_rules"` CustomHeaders []CustomHeaders `json:"custom_headers"` } diff --git a/backend/internal/database/init_sqlite.go b/backend/internal/database/init_sqlite.go index d1de047..7d0cdce 100644 --- a/backend/internal/database/init_sqlite.go +++ b/backend/internal/database/init_sqlite.go @@ -18,7 +18,10 @@ CREATE TABLE IF NOT EXISTS sites ( deploy_token TEXT NOT NULL, enabled INTEGER NOT NULL DEFAULT 1, spa INTEGER NOT NULL DEFAULT 0, - not_found_file TEXT NOT NULL DEFAULT '404.html' + not_found_file TEXT NOT NULL DEFAULT '404.html', + index_file TEXT NOT NULL DEFAULT 'index.html', + trailing_slash INTEGER DEFAULT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS forward_rules ( diff --git a/backend/internal/database/site_sqlite.go b/backend/internal/database/site_sqlite.go index 49c44d4..22105d7 100644 --- a/backend/internal/database/site_sqlite.go +++ b/backend/internal/database/site_sqlite.go @@ -25,7 +25,7 @@ var _ repository.SiteRepository = (*SQLiteSiteRepository)(nil) func (r *SQLiteSiteRepository) GetSite(id string) (*models.Site, error) { row := r.db.QueryRow(` - SELECT id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file + SELECT id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file, index_file, trailing_slash, created_at FROM sites WHERE id = ?`, id) s, err := scanSite(row) @@ -41,7 +41,7 @@ func (r *SQLiteSiteRepository) GetSite(id string) (*models.Site, error) { func (r *SQLiteSiteRepository) GetSiteByDomain(domain string) (*models.Site, error) { row := r.db.QueryRow(` - SELECT id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file + SELECT id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file, index_file, trailing_slash, created_at FROM sites WHERE domain = ?`, domain) s, err := scanSite(row) @@ -60,7 +60,7 @@ func (r *SQLiteSiteRepository) GetSiteByDomain(domain string) (*models.Site, err func (r *SQLiteSiteRepository) ListSites() ([]models.Site, error) { rows, err := r.db.Query(` - SELECT id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file + SELECT id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file, index_file, trailing_slash, created_at FROM sites`) if err != nil { return nil, fmt.Errorf("list sites: %w", err) @@ -98,10 +98,10 @@ func (r *SQLiteSiteRepository) CreateSite(s *models.Site) error { s.ID = uuid.NewString() _, err = tx.Exec(` - INSERT INTO sites (id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + INSERT INTO sites (id, name, git_server, owner, repository, branch, domain, deploy_token, spa, enabled, not_found_file, index_file, trailing_slash) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, s.ID, s.Name, s.GitServer, s.Owner, s.Repository, s.Branch, - s.Domain, s.DeployToken, s.Spa, s.Enabled, s.NotFoundFile, + s.Domain, s.DeployToken, s.Spa, s.Enabled, s.NotFoundFile, s.IndexFile, s.TrailingSlash, ) if err != nil { return fmt.Errorf("create site insert: %w", err) @@ -131,9 +131,10 @@ func (r *SQLiteSiteRepository) UpdateSite(s *models.Site) error { _, err = tx.Exec(` UPDATE sites SET name=?, git_server=?, owner=?, repository=?, branch=?, domain=?, - deploy_token=?, spa=?, enabled=?, not_found_file=? WHERE id=?`, + deploy_token=?, spa=?, enabled=?, not_found_file=?, index_file=?, trailing_slash=? WHERE id=?`, s.Name, s.GitServer, s.Owner, s.Repository, s.Branch, s.Domain, - s.DeployToken, s.Spa, s.Enabled, s.NotFoundFile, s.ID, + s.DeployToken, s.Spa, s.Enabled, s.NotFoundFile, s.IndexFile, s.TrailingSlash, + s.ID, ) if err != nil { return fmt.Errorf("update site: %w", err) @@ -364,6 +365,7 @@ func scanSite(s scanner) (*models.Site, error) { err := s.Scan( &site.ID, &site.Name, &site.GitServer, &site.Owner, &site.Repository, &site.Branch, &site.Domain, &site.DeployToken, &site.Spa, &enabled, &site.NotFoundFile, + &site.IndexFile, &site.TrailingSlash, &site.CreatedAt, ) if err != nil { return nil, err diff --git a/frontend/src/api/types/site.ts b/frontend/src/api/types/site.ts index 4c440be..29d350e 100644 --- a/frontend/src/api/types/site.ts +++ b/frontend/src/api/types/site.ts @@ -31,7 +31,9 @@ export interface Site { enabled: boolean; spa: boolean; not_found_file: string; - last_deployed: string; + index_file: string; + trailing_slash: boolean | null; + created_at: string; forward_rules: ForwardRule[]; custom_headers: CustomHeaders[]; } @@ -45,6 +47,8 @@ export interface CreateSiteRequest { domain: string; enabled: boolean; spa: boolean; + not_found_file: string; + index_file: string; } export interface GetAllSitesResponse { diff --git a/frontend/src/pages/NewSite/NewSite.tsx b/frontend/src/pages/NewSite/NewSite.tsx index 577a40e..c18888c 100644 --- a/frontend/src/pages/NewSite/NewSite.tsx +++ b/frontend/src/pages/NewSite/NewSite.tsx @@ -100,6 +100,8 @@ const NewSite = () => { domain, spa, enabled: true, + not_found_file: '', + index_file: '', }); console.log('Created site:', data); }; diff --git a/frontend/src/pages/SiteOverview/OverviewTab.tsx b/frontend/src/pages/SiteOverview/OverviewTab.tsx index 3eb7487..4b4121a 100644 --- a/frontend/src/pages/SiteOverview/OverviewTab.tsx +++ b/frontend/src/pages/SiteOverview/OverviewTab.tsx @@ -79,7 +79,7 @@ const OverviewTab = ({ site }: { site: Site }) => (
Last Deployed - {formatDate(site.last_deployed)} + {formatDate('2024-01-01T12:00:00Z')}
diff --git a/frontend/src/pages/SiteOverview/SettingsTab.tsx b/frontend/src/pages/SiteOverview/SettingsTab.tsx index c0d796a..f6c481f 100644 --- a/frontend/src/pages/SiteOverview/SettingsTab.tsx +++ b/frontend/src/pages/SiteOverview/SettingsTab.tsx @@ -35,6 +35,9 @@ const SettingsTab = ({ site }: { site: Site }) => { const [domain, setDomain] = useState(site.domain); const [branch, setBranch] = useState(site.branch); const [spa, setSpa] = useState(site.spa); + const [notFoundFile, setNotFoundFile] = useState(site.not_found_file); + const [indexFile, setIndexFile] = useState(site.index_file); + const [confirmName, setConfirmName] = useState(''); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); @@ -44,6 +47,8 @@ const SettingsTab = ({ site }: { site: Site }) => { domain, branch, spa, + not_found_file: notFoundFile, + index_file: indexFile, enabled: site.enabled, git_server: site.git_server, owner: site.owner, @@ -74,6 +79,7 @@ const SettingsTab = ({ site }: { site: Site }) => { setName(e.target.value)} /> @@ -82,6 +88,7 @@ const SettingsTab = ({ site }: { site: Site }) => { setDomain(e.target.value)} /> @@ -90,9 +97,30 @@ const SettingsTab = ({ site }: { site: Site }) => { setBranch(e.target.value)} /> +
+
+ + setIndexFile(e.target.value)} + /> +
+
+ + setNotFoundFile(e.target.value)} + /> +
+