{"id":50684191,"url":"https://github.com/wintermeyer/vutuv","last_synced_at":"2026-06-08T21:02:40.135Z","repository":{"id":42211968,"uuid":"57192889","full_name":"wintermeyer/vutuv","owner":"wintermeyer","description":"vutuv is a business network. Think of it as a fast, secure and less annoying open-source alternative for LinkedIn or XING.","archived":false,"fork":false,"pushed_at":"2026-06-02T14:53:16.000Z","size":4267,"stargazers_count":316,"open_issues_count":9,"forks_count":38,"subscribers_count":14,"default_branch":"main","last_synced_at":"2026-06-02T15:24:22.758Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://vutuv.de","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wintermeyer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-04-27T07:25:37.000Z","updated_at":"2026-06-02T14:54:34.000Z","dependencies_parsed_at":"2023-02-05T02:00:21.611Z","dependency_job_id":null,"html_url":"https://github.com/wintermeyer/vutuv","commit_stats":null,"previous_names":["vutuv/vutuv3","wintermeyer/vutuv"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wintermeyer/vutuv","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintermeyer%2Fvutuv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintermeyer%2Fvutuv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintermeyer%2Fvutuv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintermeyer%2Fvutuv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wintermeyer","download_url":"https://codeload.github.com/wintermeyer/vutuv/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wintermeyer%2Fvutuv/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34080026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-08T21:02:37.859Z","updated_at":"2026-06-08T21:02:40.128Z","avatar_url":"https://github.com/wintermeyer.png","language":"Elixir","funding_links":[],"categories":["Elixir"],"sub_categories":[],"readme":"# vutuv\n\nvutuv is a free, fast and open source social network service to host and share information about humans and organizations. It's hosted at https://vutuv.de.\n\nWe use [MIT License](https://mit-license.org/).\n\n## Development Setup\n\nvutuv is a [Phoenix Framework](https://www.phoenixframework.org/) 1.8 application. Install the following prerequisites using [mise](https://mise.jdx.dev/) (see `.tool-versions`):\n\n- Erlang 27.3.4.8\n- Elixir 1.20.0-rc.6-otp-27\n- [PostgreSQL](https://www.postgresql.org/) 17\n\n### Secret config\n\nCreate `config/dev.secret.exs`:\n```elixir\nimport Config\n\nconfig :vutuv, VutuvWeb.Endpoint,\n  secret_key_base: \"generate-with-mix-phx-gen-secret\"\n```\n\n### Start the application\n\n```bash\nmix deps.get\nmix assets.setup    # install esbuild + tailwind\nmix ecto.create\nmix ecto.migrate\nmix phx.server\n```\n\nVisit http://localhost:4000.\n\n### Email in development\n\nEmails are displayed in the browser via Swoosh's mailbox preview at http://localhost:4000/sent_emails.\n\nEvery vutuv email is machine-generated, so all of it carries the `Auto-Submitted: auto-generated` (RFC 3834) and `X-Auto-Response-Suppress: All` headers to keep out-of-office and other auto-responders silent. Mail is built from `Vutuv.Notifications.Emailer.base_email/0` and sent through the single `Emailer.deliver/1` chokepoint, the only place allowed to call `Vutuv.Mailer.deliver/1`.\n\n### Admin access\n\nFlag your account as admin:\n```sql\nUPDATE users SET administrator = true WHERE id = \u003cuser_id\u003e;\n```\n\nAdmin panel: http://localhost:4000/admin\n\n## Architecture\n\n- **Views**: Phoenix 1.8 HTML modules with `embed_templates` (no `phoenix_view` dependency)\n- **Routes**: Verified routes (`~p\"...\"` sigils)\n- **Forms**: `\u003c.form\u003e` component with `\u003c.inputs_for\u003e` for nested forms\n- **Assets**: esbuild + Tailwind CSS v4\n- **HTTP server**: Bandit\n- **Email**: Swoosh with compile-time EEx text templates; all mail built from `Emailer.base_email/0` and sent through one `Emailer.deliver/1` chokepoint that stamps the auto-generated robot headers\n- **Images**: avatars and URL screenshots are stored on local disk and resized with [`image`](https://hex.pm/packages/image) (libvips); see `Vutuv.Avatar` / `Vutuv.Screenshot`\n- **URL screenshots**: rendered by local headless Chromium, wrapped in a browser window frame (`Vutuv.BrowserFrame`) and stored as WebP; see `Vutuv.PageScreenshot`. Needs a `chromium`/`chrome` binary on the host (set `CHROMIUM_PATH` if it is not on `$PATH`)\n\n### Context modules\n\nBusiness logic is organized into Phoenix context modules under `lib/vutuv/`:\n\n| Context | Schemas | Purpose |\n|---|---|---|\n| `Vutuv.Accounts` | User, Email, Slug, SearchTerm, OAuthProvider, LoginPin, Locale, Exonym | Registration, PIN-based authentication, user management |\n| `Vutuv.Profiles` | Address, PhoneNumber, SocialMediaAccount, Url, WorkExperience, UserSkill, Skill, Endorsement | User profile data |\n| `Vutuv.Social` | Connection, Group, Membership | Following, groups |\n| `Vutuv.Tags` | Tag, UserTag, UserTagEndorsement | Tagging and endorsements |\n| `Vutuv.Recruiting` | RecruiterPackage, RecruiterSubscription, Coupon | Recruiter subscriptions |\n| `Vutuv.JobPostings` | JobPosting, JobPostingTag | Job listings |\n| `Vutuv.Search` | SearchQuery, SearchQueryRequester, SearchQueryResult | Search functionality |\n| `Vutuv.Notifications` | Emailer, Cronjob | Email notifications |\n\n## Running tests\n\n```bash\nmix test\n```\n\n## Deployment\n\nDeployment is automatic. Two GitHub Actions workflows drive it:\n\n- **CI** (`.github/workflows/ci.yml`) runs `mix precommit` (compile with `--warnings-as-errors`, unused-deps, format, `credo --strict`, tests) on every pull request and on pushes to `main`.\n- **Deploy** (`.github/workflows/deploy.yml`) runs on every push to `main`. So **merging or pushing anything to `main` ships it to production**; there is no separate deploy command.\n\nThe Deploy job runs on the self-hosted `vutuv3` runner (on bremen2) and executes `scripts/deploy.sh`, which builds a `prod` release, runs migrations against `vutuv3_prod`, atomically flips the `current` symlink, and restarts the `vutuv3` systemd service. A `deploy-production` concurrency group ensures two production deploys never overlap. nginx is not touched by the script.\n\n## Maintenance / ops tasks\n\nThese tasks operate on the on-disk uploads under `\u003cUPLOADS_DIR_PREFIX\u003e/...` (see `config/runtime.exs`). They are meant to be run manually on the server.\n\n- `mix avatar.optimize` re-compresses the large JPEG avatar variants in `\u003cUPLOADS_DIR_PREFIX\u003e/avatars/`. Requires the ImageMagick `convert` and `guetzli` binaries on the host's `$PATH`.\n- `mix urls.create_screenshots` (re)renders URL screenshots. Needs the headless Chromium binary already described above (set `CHROMIUM_PATH` if it is not on `$PATH`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwintermeyer%2Fvutuv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwintermeyer%2Fvutuv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwintermeyer%2Fvutuv/lists"}