{"id":39473424,"url":"https://github.com/4irl/urls4irl","last_synced_at":"2026-04-23T03:00:40.062Z","repository":{"id":39059627,"uuid":"455371175","full_name":"4IRL/urls4irl","owner":"4IRL","description":"Sharing URL's with friends.","archived":false,"fork":false,"pushed_at":"2026-04-17T15:44:16.000Z","size":9890,"stargazers_count":3,"open_issues_count":25,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-17T17:35:38.384Z","etag":null,"topics":["webapp"],"latest_commit_sha":null,"homepage":"https://urls.4irl.app","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/4IRL.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":null,"security":null,"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":"2022-02-04T00:09:11.000Z","updated_at":"2026-04-17T15:44:18.000Z","dependencies_parsed_at":"2024-01-30T17:41:43.168Z","dependency_job_id":"f4e483d6-b339-48f9-9078-eca0f93407a7","html_url":"https://github.com/4IRL/urls4irl","commit_stats":null,"previous_names":["4irl/urls4irl"],"tags_count":50,"template":false,"template_full_name":null,"purl":"pkg:github/4IRL/urls4irl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4IRL%2Furls4irl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4IRL%2Furls4irl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4IRL%2Furls4irl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4IRL%2Furls4irl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/4IRL","download_url":"https://codeload.github.com/4IRL/urls4irl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4IRL%2Furls4irl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32163851,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-23T02:19:40.750Z","status":"ssl_error","status_checked_at":"2026-04-23T02:17:55.737Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["webapp"],"created_at":"2026-01-18T04:56:31.570Z","updated_at":"2026-04-23T03:00:40.056Z","avatar_url":"https://github.com/4IRL.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# URLS4IRL\n\n*A simple, clean way to permanently save and share URLs.*\n\nURLS4IRL is a collaborative URL sharing platform where users organize links into shared collections called **UTubs**. Try it at [urls.4irl.app](https://urls.4irl.app).\n\n## Features\n\n- **UTubs** - Create, edit, and delete shared URL collections with names and descriptions\n- **URLs** - Add URLs with custom titles; automatic validation and canonicalization\n- **Tags** - Organize URLs with up to 5 tags per URL, scoped per UTub\n- **Members** - Invite users with role-based access (Creator, Co-Creator, Member)\n- **Accounts** - Register with email validation, login, and password reset\n\n## Tech Stack\n\n| Category | Technologies |\n|---|---|\n| **Backend** | Flask, SQLAlchemy, PostgreSQL, Redis |\n| **Frontend** | Jinja2, Vite, ES6 modules, jQuery |\n| **Auth** | Flask-Login, Flask-WTF (CSRF), Mailjet (transactional email) |\n| **Infrastructure** | Docker, Docker Compose, Gunicorn, Nginx |\n| **Testing** | pytest, Selenium, Vitest |\n| **Code Quality** | Black, Flake8, Prettier, ESLint, pre-commit |\n\n## Getting Started\n\n### Prerequisites\n\n**Recommended:** Docker \u0026 Docker Compose\n\n**Without Docker:** Python 3.11, PostgreSQL, Redis, Node.js\n\n### Environment Variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `SECRET_KEY` | Yes | - | Flask secret key for session encryption |\n| `POSTGRES_USER` | Yes | - | PostgreSQL username |\n| `POSTGRES_PASSWORD` | Yes | - | PostgreSQL password |\n| `POSTGRES_DB` | Yes | - | PostgreSQL database name |\n| `MAILJET_API_KEY` | Yes | - | Mailjet API key for transactional emails |\n| `MAILJET_SECRET_KEY` | Yes | - | Mailjet secret key |\n| `POSTGRES_TEST_DB` | No | - | Test database name for pytest |\n| `REDIS_URI` | No | `memory://` | Redis connection URI |\n| `SELENIUM_URL` | No | - | Selenium Grid URL for UI tests |\n| `ENABLE_SSL` | No | `false` | Enable HTTPS in local dev (Flask + Vite) |\n| `VITE_URL` | No | `http://localhost:5173` | Vite dev server URL (use `https://` when `ENABLE_SSL=true`) |\n\nSee [`backend/config.py`](backend/config.py) for the full list.\n\n### Running with Docker (recommended)\n\nA `Makefile` is provided for common development tasks:\n\n| Command | Description |\n|---|---|\n| `make up` | Build and start the full stack |\n| `make down` | Stop the stack |\n| `make build` | Rebuild images without starting |\n| `make restart c=\u003cservice\u003e` | Restart a specific compose service (e.g. `make restart c=web`) |\n| `make test-integration` | Run all non-UI integration tests |\n| `make test-functional` | Run all UI/Selenium functional tests |\n| `make test-js` | Run all JS unit tests (vitest) |\n| `make test-marker m=\u003cmarker\u003e` | Run tests for a specific pytest marker (e.g. `make test-marker m=utubs`) |\n| `make test-integration-parallel [n=4]` | Run all non-UI integration tests in parallel (preferred) |\n| `make test-ui-parallel [n=8]` | Run all UI/Selenium tests in parallel (preferred, max n=8) |\n| `make test-marker-parallel m=\u003cmarker\u003e [n=4]` | Run tests for a specific marker in parallel (preferred) |\n| `make vite-build` | Build Vite to verify no import/syntax errors |\n| `make help` | List all available make commands |\n\nOr run directly:\n\n```bash\ndocker compose --project-directory . -f docker/compose.local.yaml up --build --remove-orphans\n```\n\n- Flask: `http://localhost:8659`\n- Vite: `http://localhost:5173`\n\n**Note:** SSL is disabled by default in local development. To enable HTTPS and avoid mixed content warnings:\n1. Set `ENABLE_SSL=true` for both `web` and `vite` services in `docker/compose.local.yaml`\n2. Change `VITE_URL=https://localhost:5173` in the `web` service environment\n\n### Running without Docker\n\n```bash\nflask db upgrade\nflask shorturls add\nflask run --host=0.0.0.0 --port=5000 --cert=adhoc\n```\n\nOptionally populate test data:\n\n```bash\nflask addmock all\n```\n\n### Logging In\n\n1. Register on the splash page\n2. Validate your email via the confirmation link\n3. Log in to be redirected to `/home` to manage UTubs\n\nFor local development, `flask addmock all` creates mock users and data.\n\n### Vendor JS Bundles\n\nThe project uses jQuery and Bootstrap loaded as global `\u003cscript\u003e` tags. For offline development:\n\n1. Download vendor bundles:\n   ```bash\n   ./frontend/setup-vendor.sh\n   ```\n\n2. (Optional) Enable offline mode by setting environment variable:\n   ```bash\n   USE_LOCAL_JS_BUNDLES=true\n   ```\n\n**Normal mode** (default): Loads from CDN with automatic fallback to local bundles if CDN fails.\n\n**Offline mode**: Skips CDN entirely, loads local bundles only.\n\n## Testing\n\n```bash\npytest                   # all tests\npytest -m unit           # unit tests only\npytest -m splash         # auth integration tests\npytest -k \"test_name\"    # specific test\n```\n\nUI tests require Selenium (`SELENIUM_URL` env var). See [`pytest.ini`](pytest.ini) for the full list of test markers.\n\n## Project Structure\n\n```\nurls4irl/\n├── backend/          # Flask app (blueprints, models, templates, static)\n├── frontend/         # Vite/ES6 frontend modules\n├── tests/            # pytest suite (unit, integration, UI)\n├── docker/           # Dockerfiles and compose configs\n├── migrations/       # Alembic database migrations\n├── requirements/     # Python dependencies (dev, prod, test)\n└── nginx/            # Nginx config (production)\n```\n\n## API Documentation\n\nSee [`backend/API_DOCUMENTATION.md`](backend/API_DOCUMENTATION.md) for full endpoint documentation.\n\n## Contributing\n\n1. Fork the repo and create a feature branch\n2. Run `pre-commit run --all-files` before submitting\n3. Run `pytest` to verify tests pass\n4. Follow existing style (Black for Python, Prettier for JS)\n5. Open a pull request\n\n## License\n\n[GPLv3](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4irl%2Furls4irl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F4irl%2Furls4irl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4irl%2Furls4irl/lists"}