package main import ( "fmt" "io" "log" "net/http" "os" "path/filepath" "strings" md "github.com/JohannesKaufmann/html-to-markdown" "github.com/JohannesKaufmann/html-to-markdown/plugin" "github.com/gomarkdown/markdown" "github.com/microcosm-cc/bluemonday" ) type Template struct { TemplateName string `json:"templateName"` Filename string `json:"filename"` } func EditFile(w http.ResponseWriter, r *http.Request) { userSession, err := validateSession(w, r) if err != nil { handleError("Error validating session", err) return } // Get template and filename from query parameters templateName := r.URL.Query().Get("template") filename := r.URL.Query().Get("filename") filePath := filepath.Join(fileUploadPath, userSession.username, filename) // Read file content fileContent, err := readFileContent(filePath) if err != nil { handleError("Error reading file content", err) return } // Apply template content if specified if templateName != "" { templatePath := filepath.Join(templateFilePath, templateName) templateContent, err := readFileContent(templatePath) if err != nil { handleError("Error reading template content", err) return } // overwrites file content. Could also change this to append. //TODO: Give user setting to choose which method of usage. fileContent = markdown.ToHTML([]byte(templateContent), nil, nil) } // Sanitize file content sanitizedContent := sanitizeHTML(fileContent) // Convert file content based on file type if filepath.Ext(filename) == ".md" { // Convert Markdown to HTML htmlContent := markdown.ToHTML([]byte(sanitizedContent), nil, nil) fileContent = []byte(htmlContent) } else if filepath.Ext(filename) == ".html" { // Sanitize HTML content to prevent any malicious code fileContent = []byte(sanitizedContent) } templates, err := getTemplateList() if err != nil { handleError("Error getting template list", err) return } // Update the Templates slice to assign the current filename for i := range templates { templates[i].Filename = filename } data := struct { Filename string FileContent string Templates []Template // Update type to []Template }{ Filename: filename, FileContent: string(fileContent), Templates: templates, // Assign the template list } renderTemplate(w, "edit.html", data) } func SaveFile(w http.ResponseWriter, r *http.Request) { userSession, err := validateSession(w, r) if err != nil { handleError("Error validating session", err) return } // Get filename from query parameters filename := r.URL.Query().Get("filename") newFilename := r.FormValue("filename") // Retrieve the new filename from the form filePath := filepath.Join(fileUploadPath, userSession.username, filename) newFilePath := filepath.Join(fileUploadPath, userSession.username, newFilename) // Create the new file path // If the new filename is different from the expected filename if newFilename != filename { // Check if the file with the new filename already exists _, err = os.Stat(newFilePath) if err == nil { // File with new filename already exists, generate a new unique filename count := 1 extension := filepath.Ext(newFilename) filenameWithoutExt := strings.TrimSuffix(newFilename, extension) // Keep incrementing the count until a unique filename is found for err == nil { newFilename = fmt.Sprintf("%s_%d%s", filenameWithoutExt, count, extension) newFilePath = filepath.Join(fileUploadPath, userSession.username, newFilename) _, err = os.Stat(newFilePath) count++ } } else if !os.IsNotExist(err) { // Error accessing the file, handle the error or return an error response handleError("Error: Unable to access file", err) return } err := updateFilename(userSession.username, filename, newFilename) if err != nil { handleError("Error updating filename", err) return } err = os.Rename(filePath, newFilePath) // Rename the file to the new filename if err != nil { handleError("Error renaming file", err) return } } // Create or open the file file, err := os.Create(newFilePath) if err != nil { handleError("Error creating file", err) return } defer file.Close() // Read the edited content from the request form editedContent := r.FormValue("editor") // Convert edited content from HTML to Markdown if the file extension is .md if strings.HasSuffix(newFilename, ".md") { editedContent = convertHTMLtoMarkdown(editedContent) } // Write the edited content to the file _, err = file.WriteString(editedContent) if err != nil { handleError("Error writing edited content to file", err) return } // Update the edited timestamp for the user and file UpdateEditedTimestamp(userSession.username, newFilename) http.Redirect(w, r, "/home", http.StatusSeeOther) } func handleError(errMsg string, err error) { log.Printf("%s: %v", errMsg, err) } func readFileContent(filePath string) ([]byte, error) { file, err := os.Open(filePath) if err != nil { return nil, err } defer file.Close() return io.ReadAll(file) } func sanitizeHTML(htmlContent []byte) []byte { return bluemonday.UGCPolicy().SanitizeBytes(htmlContent) } func convertHTMLtoMarkdown(html string) string { options := md.Options{ EscapeMode: "disabled", } converter := md.NewConverter("", true, &options) converter.Use(plugin.Table()) converter.Use(plugin.Strikethrough("~~")) markdown, err := converter.ConvertString(html) if err != nil { log.Fatal(err) } return markdown } func getTemplateList() ([]Template, error) { dir, err := os.Open(templateFilePath) if err != nil { return nil, err } defer dir.Close() fileInfos, err := dir.Readdir(-1) if err != nil { return nil, err } templates := make([]Template, 0) for _, fileInfo := range fileInfos { if !fileInfo.IsDir() { template := Template{ TemplateName: fileInfo.Name(), Filename: "", } templates = append(templates, template) } } return templates, nil }