Last active
August 21, 2026 06:42
-
-
Save fiftin/9f754f8dc939d6a29d3c6f20740f6442 to your computer and use it in GitHub Desktop.
Interview
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| type Executor struct { | |
| maxParallel int | |
| running int | |
| results map[int]error | |
| } | |
| func NewExecutor(maxParallel int) *Executor { | |
| return &Executor{ | |
| maxParallel: maxParallel, | |
| results: make(map[int]error), | |
| } | |
| } | |
| func (e *Executor) Run(ctx context.Context, tasks []Task) map[int]error { | |
| jobs := make(chan Task) | |
| done := make(chan struct{}) | |
| go func() { | |
| for _, task := range tasks { | |
| jobs <- task | |
| } | |
| close(jobs) | |
| }() | |
| for i := 0; i < e.maxParallel; i++ { | |
| go func() { | |
| for task := range jobs { | |
| e.running++ | |
| err := executeTask(context.Background(), task) | |
| e.results[task.ID] = err | |
| e.running-- | |
| } | |
| done <- struct{}{} | |
| }() | |
| } | |
| for i := 0; i < e.maxParallel; i++ { | |
| <-done | |
| } | |
| return e.results | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package api | |
| import ( | |
| "database/sql" | |
| "encoding/json" | |
| "net/http" | |
| "net/smtp" | |
| "semaphore/utils" | |
| ) | |
| var db *sql.DB | |
| func init() { | |
| var err error | |
| db, err = sql.Open("mysql", "root:s3cr3t@tcp(10.0.0.5:3306)/semaphore") | |
| if err != nil { | |
| panic(err) | |
| } | |
| } | |
| type User struct { | |
| ID int `json:"id"` | |
| Email string `json:"email"` | |
| Password string `json:"password"` | |
| PasswordHash string `json:"password_hash"` | |
| IsAdmin bool `json:"is_admin"` | |
| } | |
| func CreateUserHandler(w http.ResponseWriter, r *http.Request) { | |
| var u User | |
| json.NewDecoder(r.Body).Decode(&u) | |
| var count int | |
| db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count) | |
| if count >= 10 { // лимит бесплатной версии | |
| http.Error(w, "license limit reached", 402) | |
| return | |
| } | |
| u.PasswordHash = utils.Hash(u.Password) | |
| res, err := db.Exec( | |
| "INSERT INTO users (email, password_hash, is_admin) VALUES (?, ?, ?)", | |
| u.Email, u.PasswordHash, u.IsAdmin) | |
| if err != nil { | |
| http.Error(w, err.Error(), 500) | |
| return | |
| } | |
| id, _ := res.LastInsertId() | |
| u.ID = int(id) | |
| smtp.SendMail("10.0.0.7:25", nil, "noreply@semaphore.local", | |
| []string{u.Email}, []byte("Welcome!")) | |
| json.NewEncoder(w).Encode(u) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package notify | |
| import "fmt" | |
| // SendNotification отправляет уведомление о завершении задачи. | |
| // kind: "email", "slack" или "telegram". | |
| func SendNotification(kind string, taskID string, status string, | |
| emailAddr string, smtpHost string, | |
| slackWebhook string, slackChannel string, | |
| telegramToken string, telegramChatID string) error { | |
| var text string | |
| if status == "success" { | |
| text = "Task " + taskID + " finished successfully" | |
| } else { | |
| text = "Task " + taskID + " failed" | |
| } | |
| if kind == "email" { | |
| if emailAddr == "" { | |
| return fmt.Errorf("email address is empty") | |
| } | |
| subject := "Semaphore: task " + taskID | |
| // ... отправка письма через smtpHost | |
| fmt.Println("email to", emailAddr, "via", smtpHost, ":", subject, "/", text) | |
| return nil | |
| } else if kind == "slack" { | |
| if slackWebhook == "" { | |
| return fmt.Errorf("slack webhook is empty") | |
| } | |
| msg := ":rocket: " + text | |
| if status != "success" { | |
| msg = ":x: " + text | |
| } | |
| // ... POST в slackWebhook | |
| fmt.Println("slack to", slackChannel, ":", msg) | |
| return nil | |
| } else if kind == "telegram" { | |
| if telegramToken == "" { | |
| return fmt.Errorf("telegram token is empty") | |
| } | |
| // ... вызов Telegram Bot API | |
| fmt.Println("telegram to", telegramChatID, ":", text) | |
| return nil | |
| } | |
| return fmt.Errorf("unknown notification kind: %s", kind) | |
| } | |
| // NotifyAll вызывается после завершения каждой задачи. | |
| func NotifyAll(taskID, status string, cfg Config) { | |
| if cfg.EmailEnabled { | |
| SendNotification("email", taskID, status, | |
| cfg.EmailAddr, cfg.SMTPHost, "", "", "", "") | |
| } | |
| if cfg.SlackEnabled { | |
| SendNotification("slack", taskID, status, | |
| "", "", cfg.SlackWebhook, cfg.SlackChannel, "", "") | |
| } | |
| if cfg.TelegramEnabled { | |
| SendNotification("telegram", taskID, status, | |
| "", "", "", "", cfg.TelegramToken, cfg.TelegramChatID) | |
| } | |
| } | |
| type Config struct { | |
| EmailEnabled bool | |
| EmailAddr string | |
| SMTPHost string | |
| SlackEnabled bool | |
| SlackWebhook string | |
| SlackChannel string | |
| TelegramEnabled bool | |
| TelegramToken string | |
| TelegramChatID string | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment