From fddb6056a81e85e405abe7cfddbbadf8a95c6e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Dvo=C5=99=C3=A1k?= <150935816+Dvorinka@users.noreply.github.com> Date: Thu, 22 May 2025 08:50:10 +0200 Subject: [PATCH] Add files via upload --- contact-scrape-test/Dockerfile | 55 +++ contact-scrape-test/Makefile | 141 ++++++ contact-scrape-test/contact-scrape.go | 481 +++++++++++++++++++++ contact-scrape-test/contact-scrape.service | 29 ++ contact-scrape-test/contacts.xlsx | Bin 0 -> 13017 bytes go.mod | 46 +- go.sum | 135 +----- index.html | 349 ++++++--------- main.go | 65 +-- 9 files changed, 867 insertions(+), 434 deletions(-) create mode 100644 contact-scrape-test/Dockerfile create mode 100644 contact-scrape-test/Makefile create mode 100644 contact-scrape-test/contact-scrape.go create mode 100644 contact-scrape-test/contact-scrape.service create mode 100644 contact-scrape-test/contacts.xlsx diff --git a/contact-scrape-test/Dockerfile b/contact-scrape-test/Dockerfile new file mode 100644 index 0000000..e28c095 --- /dev/null +++ b/contact-scrape-test/Dockerfile @@ -0,0 +1,55 @@ +# Build stage +FROM golang:1.21-alpine AS builder + +# Install git and ca-certificates (needed for fetching dependencies) +RUN apk add --no-cache git ca-certificates + +# Set the working directory +WORKDIR /build + +# Copy go mod files +COPY go.mod go.sum ./ + +# Download dependencies +RUN go mod download + +# Copy source code +COPY contact-scrape.go ./ + +# Build the application +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o contact-scrape . + +# Runtime stage +FROM alpine:latest + +# Install ca-certificates for HTTPS requests +RUN apk --no-cache add ca-certificates tzdata + +# Create non-root user +RUN addgroup -g 1001 -S appgroup && \ + adduser -u 1001 -S appuser -G appgroup + +# Set working directory +WORKDIR /app + +# Copy binary from builder stage +COPY --from=builder /build/contact-scrape . + +# Copy HTML file +COPY index.html . + +# Create data directory +RUN mkdir -p data && chown -R appuser:appgroup /app + +# Switch to non-root user +USER appuser + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 + +# Run the application +CMD ["./contact-scrape"] \ No newline at end of file diff --git a/contact-scrape-test/Makefile b/contact-scrape-test/Makefile new file mode 100644 index 0000000..6043f99 --- /dev/null +++ b/contact-scrape-test/Makefile @@ -0,0 +1,141 @@ +# Contact Scrape Makefile + +.PHONY: build run clean install dev test help + +# Variables +BINARY_NAME=contact-scrape +BUILD_DIR=build +PORT=8080 + +help: ## Show this help message + @echo "Available commands:" + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development +dev: ## Run the application in development mode + @echo "Starting development server..." + @go run . + +run: build ## Build and run the application + @echo "Starting $(BINARY_NAME)..." + @./$(BUILD_DIR)/$(BINARY_NAME) + +test: ## Run tests + @echo "Running tests..." + @go test -v ./... + +##@ Build +build: ## Build the application + @echo "Building $(BINARY_NAME)..." + @mkdir -p $(BUILD_DIR) + @go build -o $(BUILD_DIR)/$(BINARY_NAME) . + @echo "Binary built: $(BUILD_DIR)/$(BINARY_NAME)" + +build-linux: ## Build for Linux (useful for deployment) + @echo "Building $(BINARY_NAME) for Linux..." + @mkdir -p $(BUILD_DIR) + @GOOS=linux GOARCH=amd64 go build -o $(BUILD_DIR)/$(BINARY_NAME)-linux . + @echo "Linux binary built: $(BUILD_DIR)/$(BINARY_NAME)-linux" + +##@ Dependencies +deps: ## Download dependencies + @echo "Downloading dependencies..." + @go mod download + @go mod tidy + +##@ Deployment +install: build ## Install the service (requires sudo) + @echo "Installing contact-scrape service..." + @sudo mkdir -p /opt/contact-scrape + @sudo cp $(BUILD_DIR)/$(BINARY_NAME) /opt/contact-scrape/ + @sudo cp index.html /opt/contact-scrape/ + @sudo mkdir -p /opt/contact-scrape/data + @sudo chown -R www-data:www-data /opt/contact-scrape + @sudo cp contact-scrape.service /etc/systemd/system/ + @sudo systemctl daemon-reload + @sudo systemctl enable contact-scrape + @echo "Service installed. Start with: sudo systemctl start contact-scrape" + +uninstall: ## Uninstall the service (requires sudo) + @echo "Uninstalling contact-scrape service..." + @sudo systemctl stop contact-scrape 2>/dev/null || true + @sudo systemctl disable contact-scrape 2>/dev/null || true + @sudo rm -f /etc/systemd/system/contact-scrape.service + @sudo systemctl daemon-reload + @sudo rm -rf /opt/contact-scrape + @echo "Service uninstalled." + +status: ## Check service status + @sudo systemctl status contact-scrape + +start: ## Start the service + @sudo systemctl start contact-scrape + +stop: ## Stop the service + @sudo systemctl stop contact-scrape + +restart: ## Restart the service + @sudo systemctl restart contact-scrape + +logs: ## View service logs + @sudo journalctl -u contact-scrape -f + +##@ File Management +upload-xlsx: ## Upload contacts.xlsx to server (set SERVER variable) + @if [ -z "$(SERVER)" ]; then echo "Usage: make upload-xlsx SERVER=user@hostname"; exit 1; fi + @echo "Uploading contacts.xlsx to $(SERVER)..." + @scp contacts.xlsx $(SERVER):/opt/contact-scrape/ + @ssh $(SERVER) "sudo chown www-data:www-data /opt/contact-scrape/contacts.xlsx" + @ssh $(SERVER) "sudo systemctl restart contact-scrape" + @echo "File uploaded and service restarted." + +##@ Monitoring +monitor: ## Monitor the service with file watching + @echo "Monitoring contacts.xlsx for changes..." + @while true; do \ + inotifywait -e modify contacts.xlsx 2>/dev/null && \ + echo "File changed, reloading..." && \ + curl -X POST http://localhost:$(PORT)/reload; \ + sleep 1; \ + done + +##@ Utilities +clean: ## Clean build artifacts + @echo "Cleaning build artifacts..." + @rm -rf $(BUILD_DIR) + @rm -f data/contacts.json + +setup-dirs: ## Create necessary directories + @mkdir -p data + @mkdir -p $(BUILD_DIR) + +check-file: ## Check if contacts.xlsx exists and show info + @if [ -f "contacts.xlsx" ]; then \ + echo "✓ contacts.xlsx found"; \ + ls -lh contacts.xlsx; \ + else \ + echo "✗ contacts.xlsx not found"; \ + echo "Please place your Excel file in the current directory"; \ + fi + +##@ Docker (Optional) +docker-build: ## Build Docker image + @echo "Building Docker image..." + @docker build -t contact-scrape . + +docker-run: ## Run in Docker container + @echo "Running Docker container..." + @docker run -p $(PORT):$(PORT) -v $(PWD)/contacts.xlsx:/app/contacts.xlsx -v $(PWD)/data:/app/data contact-scrape + +##@ Information +info: ## Show application information + @echo "Contact Scrape Application" + @echo "=========================" + @echo "Port: $(PORT)" + @echo "Binary: $(BINARY_NAME)" + @echo "Build dir: $(BUILD_DIR)" + @echo "" + @echo "Endpoints:" + @echo " http://localhost:$(PORT)/ - Web interface" + @echo " http://localhost:$(PORT)/contacts - JSON API" + @echo " http://localhost:$(PORT)/reload - Reload data (POST)" \ No newline at end of file diff --git a/contact-scrape-test/contact-scrape.go b/contact-scrape-test/contact-scrape.go new file mode 100644 index 0000000..c5e35ca --- /dev/null +++ b/contact-scrape-test/contact-scrape.go @@ -0,0 +1,481 @@ +package main + +import ( + "crypto/md5" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "regexp" + "strings" + "time" + + "github.com/xuri/excelize/v2" +) + +type Contact struct { + Name string `json:"name"` + Position string `json:"position"` + Phone string `json:"phone,omitempty"` + ServicePhone string `json:"service_phone,omitempty"` + Table int `json:"table"` // 1 for first table, 2 for second table +} + +type ContactData struct { + Contacts []Contact `json:"contacts"` + LastUpdated time.Time `json:"last_updated"` + FileHash string `json:"file_hash"` +} + +var ( + currentData *ContactData + dataFile = "data/contacts.json" + xlsxFile = "contacts.xlsx" +) + +func main() { + // Create data directory if it doesn't exist + if err := os.MkdirAll("data", 0755); err != nil { + log.Printf("Warning: Could not create data directory: %v", err) + } + + // Load existing data or parse from Excel + loadData() + + // Set up HTTP handlers + http.HandleFunc("/", serveIndex) + http.HandleFunc("/contacts", serveContacts) + http.HandleFunc("/reload", reloadData) + + // Start server + port := os.Getenv("PORT") + if port == "" { + port = "80" + } + + log.Printf("Server starting on port %s", port) + log.Printf("Access the application at: http://localhost:%s", port) + log.Fatal(http.ListenAndServe(":"+port, nil)) +} + +func loadData() { + // Check if Excel file exists + if _, err := os.Stat(xlsxFile); os.IsNotExist(err) { + log.Printf("Excel file %s not found, using empty data", xlsxFile) + currentData = &ContactData{ + Contacts: []Contact{}, + LastUpdated: time.Now(), + FileHash: "", + } + return + } + + // Calculate current file hash + currentHash, err := calculateFileHash(xlsxFile) + if err != nil { + log.Printf("Error calculating file hash: %v", err) + return + } + + // Check if cached data exists and is up to date + if cachedData, err := loadCachedData(); err == nil { + if cachedData.FileHash == currentHash { + log.Println("Using cached data (file unchanged)") + currentData = cachedData + return + } + } + + // Parse Excel file + log.Println("Parsing Excel file...") + contacts, err := parseExcelFile(xlsxFile) + if err != nil { + log.Printf("Error parsing Excel file: %v", err) + // Use empty data if parsing fails + currentData = &ContactData{ + Contacts: []Contact{}, + LastUpdated: time.Now(), + FileHash: currentHash, + } + return + } + + currentData = &ContactData{ + Contacts: contacts, + LastUpdated: time.Now(), + FileHash: currentHash, + } + + // Save to cache + if err := saveCachedData(currentData); err != nil { + log.Printf("Warning: Could not save cached data: %v", err) + } + + log.Printf("Loaded %d contacts from Excel file", len(contacts)) +} + +func calculateFileHash(filename string) (string, error) { + file, err := os.Open(filename) + if err != nil { + return "", err + } + defer file.Close() + + hash := md5.New() + if _, err := io.Copy(hash, file); err != nil { + return "", err + } + + return fmt.Sprintf("%x", hash.Sum(nil)), nil +} + +func loadCachedData() (*ContactData, error) { + file, err := os.Open(dataFile) + if err != nil { + return nil, err + } + defer file.Close() + + var data ContactData + decoder := json.NewDecoder(file) + if err := decoder.Decode(&data); err != nil { + return nil, err + } + + return &data, nil +} + +func saveCachedData(data *ContactData) error { + file, err := os.Create(dataFile) + if err != nil { + return err + } + defer file.Close() + + encoder := json.NewEncoder(file) + encoder.SetIndent("", " ") + return encoder.Encode(data) +} + +func parseExcelFile(filename string) ([]Contact, error) { + f, err := excelize.OpenFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to open Excel file: %v", err) + } + defer f.Close() + + // Get the first sheet name + sheets := f.GetSheetList() + if len(sheets) == 0 { + return nil, fmt.Errorf("no sheets found in Excel file") + } + + sheetName := sheets[0] + var contacts []Contact + + // Parse first table (A-D columns) + contacts = append(contacts, parseTable(f, sheetName, "A", "D", 1)...) + + // Parse second table (F-H columns) + contacts = append(contacts, parseTable(f, sheetName, "F", "H", 2)...) + + return contacts, nil +} + +func parseTable(f *excelize.File, sheetName, startCol, endCol string, tableNum int) []Contact { + var contacts []Contact + var currentContact *Contact + + // Get all rows in the sheet + rows, err := f.GetRows(sheetName) + if err != nil { + log.Printf("Error getting rows: %v", err) + return contacts + } + + // Skip header rows (first 3 rows based on your description) + startRow := 3 + if len(rows) <= startRow { + return contacts + } + + // Column indices + var nameCol, positionCol, phoneCol, servicePhoneCol int + if tableNum == 1 { + nameCol, positionCol, phoneCol, servicePhoneCol = 0, 1, 2, 3 // A, B, C, D + } else { + nameCol, positionCol, phoneCol = 5, 6, 7 // F, G, H + } + + for i := startRow; i < len(rows); i++ { + row := rows[i] + + // Skip if row is too short + if len(row) <= nameCol { + continue + } + + // Check for "Aktualizace" - end of data + if len(row) > nameCol && strings.Contains(strings.ToLower(row[nameCol]), "aktualizace") { + break + } + + // Check for special formatting rows (like "*02(xx)") + if len(row) > positionCol && strings.Contains(row[positionCol], "*") { + continue + } + + name := strings.TrimSpace(row[nameCol]) + position := "" + phone := "" + servicePhone := "" + + if len(row) > positionCol { + position = strings.TrimSpace(row[positionCol]) + } + if len(row) > phoneCol { + phone = strings.TrimSpace(row[phoneCol]) + } + if tableNum == 1 && len(row) > servicePhoneCol { + servicePhone = strings.TrimSpace(row[servicePhoneCol]) + } + + // Clean phone numbers + phone = cleanPhoneNumber(phone) + servicePhone = cleanPhoneNumber(servicePhone) + + // If we have a name, start a new contact + if name != "" && !strings.Contains(name, "(") { + currentContact = &Contact{ + Name: name, + Position: position, + Phone: phone, + ServicePhone: servicePhone, + Table: tableNum, + } + contacts = append(contacts, *currentContact) + } else if currentContact != nil { + // This is additional data for the current contact + newContact := *currentContact + if position != "" { + newContact.Position = position + } + if phone != "" { + newContact.Phone = phone + } + if servicePhone != "" { + newContact.ServicePhone = servicePhone + } + contacts = append(contacts, newContact) + } + } + + return contacts +} + +func cleanPhoneNumber(phone string) string { + if phone == "" { + return "" + } + + // Remove extra whitespace + phone = strings.TrimSpace(phone) + + // Remove common formatting characters + re := regexp.MustCompile(`[^\d+\-\s()]`) + phone = re.ReplaceAllString(phone, "") + + // If it's just a short number (internal extension), keep as is + if len(phone) <= 3 { + return phone + } + + // If it looks like a Czech number without country code, add it + if regexp.MustCompile(`^[67]\d{8}$`).MatchString(strings.ReplaceAll(phone, " ", "")) { + return "+420 " + phone + } + + return phone +} + +func serveIndex(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.NotFound(w, r) + return + } + + // Check if index.html exists + if _, err := os.Stat("index.html"); os.IsNotExist(err) { + // Serve embedded HTML if file doesn't exist + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, getEmbeddedHTML()) + return + } + + http.ServeFile(w, r, "index.html") +} + +func serveContacts(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "*") + + if currentData == nil { + http.Error(w, `{"error": "No data available"}`, http.StatusInternalServerError) + return + } + + encoder := json.NewEncoder(w) + encoder.SetIndent("", " ") + encoder.Encode(currentData) +} + +func reloadData(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + log.Println("Manual reload requested") + loadData() + + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"status": "reloaded", "contacts_count": %d}`, len(currentData.Contacts)) +} + +func getEmbeddedHTML() string { + return ` + + + + + Kontakty + + + +
+
+
+

Kontakty

+ +
+ +
+ +
+ +
+
+

Načítání kontaktů...

+
+ + + + +
+
+ + + +` +} diff --git a/contact-scrape-test/contact-scrape.service b/contact-scrape-test/contact-scrape.service new file mode 100644 index 0000000..ff60678 --- /dev/null +++ b/contact-scrape-test/contact-scrape.service @@ -0,0 +1,29 @@ +[Unit] +Description=Contact Scrape Service +After=network.target + +[Service] +Type=simple +User=www-data +WorkingDirectory=/opt/contact-scrape +ExecStart=/opt/contact-scrape/contact-scrape +Restart=always +RestartSec=5 +Environment=PORT=8080 + +# Security settings +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths=/opt/contact-scrape/data +CapabilityBoundingSet=CAP_NET_BIND_SERVICE +AmbientCapabilities=CAP_NET_BIND_SERVICE + +# Logging +StandardOutput=journal +StandardError=journal +SyslogIdentifier=contact-scrape + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/contact-scrape-test/contacts.xlsx b/contact-scrape-test/contacts.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..322f2d42d8b8e8eceb5b22d1acf1f60e5b088e92 GIT binary patch literal 13017 zcmeHt19xTHw)Ku}+o(9HR8+Bzif!Arjf!pCwr!{4ip>hYoOAE}&b@Wd`vvc<-S%2- zwY^4fd(An}=Nw&D0t6Hl01kiz002Y)K32aeD6bt}B0YCz)3s_q@7+N`KE4bPi z+H2CeSXvO~f&x=!1AsrC|G(pZ@d%VFj9B$DAa*Dp^9Xh-h2{j8mO|pT;;NG#LL|Gy zsgO4AV^O|%QW(i8)R|=UV!Rzbu4DfUJY+XDRTn2T3i2Q_FBimB{L?CuNI*XdES!065 zAwa-uS`Jif$wUDAIS2}|h7t9e(AWo@o@AqSw3!LIZ`H*^=R^dvHDkB3(NX*5tKUpa zgkrYLLalR)*lxB5u6+gYA=K5a5-Y|f#&bM;)I1woboweF$ypa8wvyux7StE^RKU(; z`91>oSJ@i;Mn*0l!B*lGJkHxkoV~w;0A&9oOdAv#NNzuTPx9ken2#{kwllP_r>Fbl z{D0y3zc?oU?bXZUq-6RSV1q71UxJ4p7B*rK`6ZnBMB9iIynMvg5gH=%NO3m0C~y%K zu>C6;+6YlLS}e`C5%>OB3(+YH49Ah34=YB zFswi>!7nafO^)hjyjpVZi(M~ty})HZ878Ncs-yt^5Z zbicahG2@hkkBcGJ_ce4>Q`ggpL@+*eRC4nqrmgG3&;Wb07N;v)&e0$C4j26{~_PnQy6a8AQOJdC)Wa zBq*%IG?HJTcS7ZpF_SrxV@$7Z1Z6i_jM*)$3cF}NT@*}jd7}RS4iu>c=SOs^y;#CJ z4RNOzqTzhG#=jMyfXw4{e$v%$>MN;$_-RnQ_9|AKnPV=^O=#SErzdUJvN}|n?P(yO zV_e8?$^paDkxC|leev^ccHw1cdWBno?YPWK&p{rqE?2O^)kvt9m3y|LXWFm25dutU zr|=6;onR$ljldlDVW(W929WXTZ34->w(ZRfRFTclCqD`0J}8P^)^BG}zy%)?$AOIzlRI(>){vqc za1f-pAT`vTdy5ZlZP`*dVyU7yUfP9;3bRg+d}k^P1s8$gl}(D2?&mSj8Xyc?T&0t1SmNj=kBrd$%)ZU^4QGVDKoMZw6lYH`k7K_SswKT{#hYulC<|gM+W825v=~ z0pm#eE5Fr$FJA`NT81x_(!ey_el6x-2nQabsv%R zzxycvCMD7TL2F>%f*G9CoY0U~9O#Kp6;BW#N9sSVkrRFPy4}Q~YSmO(l%NN(4syMi z7;<{}0>1--a@Rv$9EJ?)hGKE?70i0#W K@2mpfPXr7F`Skdt>Pt8Z!|O#NiE|K1|7 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 AVE_OC literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod index 8bbe393..9c8b1bd 100644 --- a/go.mod +++ b/go.mod @@ -3,36 +3,18 @@ module ppve go 1.24.2 require ( - fyne.io/systray v1.11.0 // indirect - github.com/bytedance/sonic v1.13.2 // indirect - github.com/bytedance/sonic/loader v0.2.4 // indirect - github.com/cloudwego/base64x v0.1.5 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect - github.com/gin-contrib/cors v1.7.5 // indirect - github.com/gin-contrib/sse v1.0.0 // indirect - github.com/gin-gonic/gin v1.10.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.26.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.10 // indirect - github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.12 // indirect - golang.org/x/arch v0.15.0 // indirect - golang.org/x/crypto v0.36.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect - gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/xuri/excelize/v2 v2.9.1 + gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df +) + +require ( + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.4 // indirect + github.com/tiendc/go-deepcopy v1.6.0 // indirect + github.com/xuri/efp v0.0.1 // indirect + github.com/xuri/nfp v0.0.1 // indirect + golang.org/x/crypto v0.38.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/text v0.25.0 // indirect + gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect ) diff --git a/go.sum b/go.sum index 65cae2d..45058a1 100644 --- a/go.sum +++ b/go.sum @@ -1,120 +1,33 @@ -fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= -fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= -github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= -github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= -github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= -github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= -github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk= -github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= -github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= -github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00= +github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= -golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +github.com/tiendc/go-deepcopy v1.6.0 h1:0UtfV/imoCwlLxVsyfUd4hNHnB3drXsfle+wzSCA5Wo= +github.com/tiendc/go-deepcopy v1.6.0/go.mod h1:toXoeQoUqXOOS/X4sKuiAoSk6elIdqc0pN7MTgOOo2I= +github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= +github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.9.1 h1:VdSGk+rraGmgLHGFaGG9/9IWu1nj4ufjJ7uwMDtj8Qw= +github.com/xuri/excelize/v2 v2.9.1/go.mod h1:x7L6pKz2dvo9ejrRuD8Lnl98z4JLt0TGAwjhW+EiP8s= +github.com/xuri/nfp v0.0.1 h1:MDamSGatIvp8uOmDP8FnmjuQpu90NzdJxo7242ANR9Q= +github.com/xuri/nfp v0.0.1/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/index.html b/index.html index 1670341..8f7ab0a 100644 --- a/index.html +++ b/index.html @@ -1,228 +1,123 @@ - - - - - - Aplikační Rozcestník - - - - - -
-
- -
-

Poppe + Potthoff - Firemní Aplikace

-

Rychlý přístup ke všem důležitým systémům

-
-
-
- -
- -
-
- -
- -
-
-
- - -
- -
-
- -
-

Záznam jízdy služebního vozu

-

Systém pro evidenci služebních jízd.

- - Otevřít aplikaci - -
- - -
-
- -
-

Objednávka obědů

-

Portál pro objednávku a přehled firemních obědů

- - Otevřít aplikaci - -
- - -
-
- -
-

OSTicket

-

Systém technické podpory a hlášení problémů

- - Otevřít aplikaci - -
- - -
-
- -
-

Kanboard úkolníček

-

Správa úkolů a projektů v přehledném kanban stylu

- - Otevřít aplikaci - -
- -
- - -
-
-
-
-
-
- WINDOWS SDÍLENÉ SLOŽKY -
-
-
- - -
- -
-
-
- -
-
-

Telefonní seznam

-

Aktuální telefonní seznam společnosti

-
-
- -
- - -
-
-
- -
-
-

Řízená dokumentace

-

Přístup k firemní řízené dokumentaci

-
-
- -
- - -
-
-
- -
-
-

Management úkolů

-

Systém pro správu a sledování firemních úkolů

-
-
- -
-
-
- -
-
-

© 2025 Poppe + Potthoff

-

Created by TDvorak

-
-
- - - + + + + + + Aplikační Rozcestník + + + + + +
+
+ +
+

Poppe + Potthoff - Firemní Aplikace

+

Rychlý přístup ke všem důležitým systémům

+
+
+
+ +
+ +
+
+ +
+ +
+
+
+ + +
+ +
+
+ +
+

Záznam jízdy služebního vozu

+

Systém pro evidenci služebních jízd.

+ + Otevřít aplikaci + +
+ + +
+
+ +
+

Objednávka obědů

+

Portál pro objednávku a přehled firemních obědů

+ + Otevřít aplikaci + +
+ + +
+
+ +
+

OSTicket

+

Systém technické podpory a hlášení problémů

+ + Otevřít aplikaci + +
+ + +
+
+ +
+

Kanboard

+

Správa úkolů a projektů v přehledném kanban stylu

+ + Otevřít aplikaci + +
+
+
+ +
+
+

© 2025 Poppe + Potthoff

+

Created by TDvorak

+
+
+ + + \ No newline at end of file diff --git a/main.go b/main.go index 13bb554..a287b19 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "log" "net/http" "os" - "os/exec" "strings" "time" @@ -51,11 +50,9 @@ func main() { http.ServeFile(w, r, "evidence-aut.html") })) - http.HandleFunc("/open-folder", enableCORS(handleOpenFolder)) - port := os.Getenv("PORT") if port == "" { - port = "8080" + port = "80" } log.Printf("Server běží na portu %s", port) @@ -159,66 +156,6 @@ func handleSubmit(w http.ResponseWriter, r *http.Request) { w.Write([]byte(`{"message":"Záznam byl úspěšně uložen a email odeslán"}`)) } -// handleOpenFolder opens a Windows Explorer window to the specified folder path -func handleOpenFolder(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - // Only allow POST requests - if r.Method != http.MethodPost { - if r.Method == http.MethodOptions { - w.WriteHeader(http.StatusOK) - return - } - w.WriteHeader(http.StatusMethodNotAllowed) - json.NewEncoder(w).Encode(map[string]string{"error": "Only POST method is allowed"}) - return - } - - // Parse the request body - type FolderRequest struct { - Path string `json:"path"` - } - - var req FolderRequest - err := json.NewDecoder(r.Body).Decode(&req) - if err != nil { - log.Printf("Chyba při čtení těla požadavku: %v", err) - w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(map[string]string{"error": "Failed to parse request body"}) - return - } - - // Validate the folder path - if req.Path == "" { - w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(map[string]string{"error": "Path is required"}) - return - } - - // Sanitize the path (basic security - you might want to add more validation) - // Ensure it's a valid Windows network path - if !strings.HasPrefix(req.Path, "C:\\") { - w.WriteHeader(http.StatusBadRequest) - json.NewEncoder(w).Encode(map[string]string{"error": "Only network paths on M: drive are allowed"}) - return - } - - // Construct the command to open Windows Explorer - cmd := exec.Command("explorer.exe", req.Path) - - // Run the command - err = cmd.Start() - if err != nil { - log.Printf("Chyba při spouštění explorer.exe: %v", err) - w.WriteHeader(http.StatusInternalServerError) - json.NewEncoder(w).Encode(map[string]string{"error": fmt.Sprintf("Failed to open folder: %v", err)}) - return - } - - // Send a success response - json.NewEncoder(w).Encode(map[string]string{"status": "success", "message": "Folder opened successfully"}) -} - func sendEmail(entry TripEntry, parsedDateStart, parsedDateEnd time.Time, czechMonths []string) error { smtpHost := "mail.pp-kunovice.cz" smtpPort := 465