{"id":30396161,"url":"https://github.com/datasektionen/femto","last_synced_at":"2026-04-11T03:06:18.322Z","repository":{"id":310265005,"uuid":"902444739","full_name":"datasektionen/femto","owner":"datasektionen","description":"Länkförkortare","archived":false,"fork":false,"pushed_at":"2025-08-16T21:13:30.000Z","size":19240,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-16T23:21:57.452Z","etag":null,"topics":["docker","express","link-shortener","node","postgresql","react"],"latest_commit_sha":null,"homepage":"https://admin.dsekt.se","language":"TypeScript","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/datasektionen.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}},"created_at":"2024-12-12T15:23:18.000Z","updated_at":"2025-08-16T21:13:33.000Z","dependencies_parsed_at":"2025-08-16T23:32:35.177Z","dependency_job_id":null,"html_url":"https://github.com/datasektionen/femto","commit_stats":null,"previous_names":["datasektionen/femto"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/datasektionen/femto","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datasektionen%2Ffemto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datasektionen%2Ffemto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datasektionen%2Ffemto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datasektionen%2Ffemto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datasektionen","download_url":"https://codeload.github.com/datasektionen/femto/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datasektionen%2Ffemto/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271479597,"owners_count":24766815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-21T02:00:08.990Z","response_time":74,"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":["docker","express","link-shortener","node","postgresql","react"],"created_at":"2025-08-21T12:27:06.632Z","updated_at":"2025-12-30T21:07:16.671Z","avatar_url":"https://github.com/datasektionen.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Femto - Link Shortener\n\nA simple **link-shortening** service built with **TypeScript**, **React (Vite)**, **Mantine UI**, and **Express**. It uses **OIDC** for authentication and **JWT** for API authorization. Enter a long URL, and Femto generates a shorter, more memorable link. This system is intended for internal use (e.g., linking forms, events, or other resources).\n\n---\n\n## Features\n\n- **Shorten URLs** quickly and easily via a web interface.\n- **OIDC Authentication** for user login.\n- **JWT Authorization** for protected API endpoints.\n- **View/Manage** created links (for authenticated users).\n- **View Link Statistics** (click counts, timestamps).\n- **Automatic Slug Generation** if none is provided.\n- **Custom Slug Support** (if slug is available).\n- **Optional Expiry Dates** for links.\n- **Optional Group Association** for links.\n- **QR Code Generation** for shortened links.\n- **Blacklist Management** for blocked URLs or slugs.\n- **Database-Backed** with PostgreSQL.\n\n---\n\n## Prerequisites\n\n1.  [Node.js](https://nodejs.org/) v22 or higher (for local development).\n2.  [Docker](https://www.docker.com/) (for containerized deployment).\n3.  [Docker Compose](https://docs.docker.com/compose/) (to orchestrate multiple containers).\n4.  Access to an **OIDC Provider** (like Keycloak, Auth0, or a custom one like `sso.datasektionen.se`) for authentication.\n\n---\n\n## Project Structure\n\n```\nfemto/\n├── .github/\n│   └── workflows/\n│       └── deploy-dual.yml             # GitHub Actions CI/CD configuration\n├── client/                             # Frontend React (Vite) application\n│   ├── public/                         # Static assets (icons, manifest)\n│   ├── src/                            # Source code\n│   │   ├── authorization/              # Auth context, hooks, OIDC logic\n│   │   │   ├── authApi.ts              # Authentication API calls\n│   │   │   ├── AuthContext.tsx         # React context for auth state\n│   │   │   ├── types.ts                # Auth-related TypeScript types\n│   │   │   └── useAuth.ts              # Auth hook\n│   │   ├── components/                 # Reusable React components\n│   │   │   ├── auth/                   # Authentication components\n│   │   │   │   ├── LoginRedirect.tsx\n│   │   │   │   ├── Logout.tsx\n│   │   │   │   ├── OIDCCallback.tsx\n│   │   │   │   └── ProtectedRoute.tsx\n│   │   │   └── LinkCreator.tsx         # Main link creation component\n│   │   ├── types/                      # TypeScript type definitions\n│   │   │   └── methone.d.ts            # Methone-specific types\n│   │   ├── views/                      # Page components\n│   │   │   ├── Blacklist.tsx           # Blacklist management page\n│   │   │   ├── Home.tsx                # Main homepage\n│   │   │   ├── LinkDetails.tsx         # Individual link statistics\n│   │   │   └── Links.tsx               # User's links overview\n│   │   ├── App.css                     # Application styles\n│   │   ├── App.tsx                     # Main application component\n│   │   ├── configuration.ts            # Configuration constants\n│   │   ├── index.css                   # Global styles\n│   │   ├── index.tsx                   # Application entry point\n│   │   └── vite-env.d.ts               # Vite environment types\n│   ├── .env                            # Environment variables (local)\n│   ├── .env.example                    # Example environment variables\n│   ├── .gitignore                      # Git ignore rules\n│   ├── Dockerfile.client               # Docker configuration for client\n│   ├── eslint.config.js                # ESLint configuration\n│   ├── index.html                      # HTML template\n│   ├── nginx.conf                      # Nginx configuration for Docker\n│   ├── package.json                    # Dependencies and scripts\n│   ├── tsconfig.app.json               # TypeScript config for app\n│   ├── tsconfig.json                   # Main TypeScript configuration\n│   ├── tsconfig.node.json              # TypeScript config for Node tools\n│   └── vite.config.ts                  # Vite configuration\n├── server/                             # Backend Express application\n│   ├── database/                       # Database setup and schema\n│   │   ├── insert.sql                  # Sample data insertion\n│   │   └── schema.sql                  # Database schema definition\n│   ├── src/                            # Source code\n│   │   ├── controllers/                # Request handlers\n│   │   │   ├── authController.ts       # Authentication logic\n│   │   │   ├── blacklistController.ts  # Blacklist management\n│   │   │   ├── linkController.ts       # Link CRUD operations\n│   │   │   └── statusController.ts     # Health check endpoints\n│   │   ├── middlewares/                # Express middleware\n│   │   │   └── jwtAuthMiddleware.ts    # JWT token validation\n│   │   ├── routes/                     # Route definitions\n│   │   │   ├── apiRouter.ts            # API endpoints\n│   │   │   ├── loginRouter.ts          # Authentication routes\n│   │   │   └── redirectRouter.ts       # URL redirection logic\n│   │   ├── services/                   # Business logic services\n│   │   │   ├── cleanupService.ts       # Expired link cleanup\n│   │   │   └── db.ts                   # Database connection and queries\n│   │   └── index.ts                    # Server entry point\n│   ├── .env                            # Environment variables (local)\n│   ├── .env.example                    # Example environment variables\n│   ├── .gitignore                      # Git ignore rules\n│   ├── Dockerfile.server               # Docker configuration for server\n│   ├── package.json                    # Dependencies and scripts\n│   └── tsconfig.json                   # TypeScript configuration\n├── docker-compose.yml                  # Docker Compose configuration\n├── job.nomad.hcl                       # Nomad job specification\n└── README.md                           # This file\n```\n\n---\n\n## Getting Started\n\n### 1. Clone the Repository\n\n```bash\ngit clone https://github.com/datasektionen/femto.git\ncd femto\n```\n\n### 2. Environment Variables\n\nThis project requires separate environment variables for the client and server.\n\n- Copy `server/.env.example` to `server/.env` and fill in the values.\n- Copy `client/.env.example` to `client/.env` and fill in the values.\n\n---\n\n### 3. Database Setup\n\nThe project includes SQL files for database initialization:\n\n-   `server/database/schema.sql` - Contains the database schema\n-   `server/database/insert.sql` - Contains sample data (optional)\n\nSchema setup is run automatically.\n\n---\n\n### 4. Running with Docker (Recommended)\n\nThis is the easiest way to get all services (client, server, database) running together.\n\n1.  Ensure your `server/.env` file has `POSTGRES_HOST=postgres`.\n2.  Build and start the containers:\n\n    ```bash\n    docker-compose up --build -d\n    ```\n\n    -   `-d` runs the containers in the background.\n\nThis will:\n\n-   Launch the **client** React app, accessible at [http://localhost:3000/](http://localhost:3000/)\n-   Launch the **server** Express app, accessible at [http://localhost:5000/](http://localhost:5000/)\n-   Launch a **PostgreSQL** database container, accessible to the server via the hostname `postgres`\n\n#### Connecting to the Database (Docker)\n\n1.  List running containers: `docker ps`\n2.  Find the container ID for the `postgres` image.\n3.  Connect using psql:\n\n    ```bash\n    docker exec -it [container-ID] psql -U your_db_user -d your_db_name\n    ```\n\n---\n\n## Local Development without Docker\n\nYou can also run the client and server directly on your machine. Make sure you have a separate PostgreSQL instance running and accessible.\n\n### Database Setup\n\n1.  Create a PostgreSQL database\n2.  Run the schema from `server/database/schema.sql`\n3.  Optionally run `server/database/insert.sql` for sample data\n\n### Server\n\n```bash\ncd server\nnpm install\nnpm run dev # Runs server with auto-restart on changes\n```\n\n### Client\n\n```bash\ncd client\nnpm install\nnpm run dev # Starts development server\n```\n\nThe client app should start on [http://localhost:3000/](http://localhost:3000/).\n\n---\n\n## Usage\n\n1.  **Login**: Access the client URL (e.g., [http://localhost:3000/](http://localhost:3000/)). Click the login button, which will redirect you to your OIDC provider.\n2.  **Shorten a Link**: Once logged in, use the form on the homepage to enter a long URL. You can optionally provide a custom slug, expiry date, or associate a mandate.\n3.  **View Links**: Navigate to the \"Länkar\" (Links) page to see links you have created.\n4.  **View Stats**: Click on a link to view its detailed statistics.\n5.  **Manage Blacklist**: Access the blacklist page to manage blocked URLs or slugs.\n6.  **Redirect**: Navigate to `\u003cSERVER_URL\u003e/\u003cslug\u003e` (e.g., `http://localhost:5000/myslug`) to be redirected to the original long URL.\n\n---\n\n## FAQ\n\n-   **Why is the client showing a blank page or login errors?**\n    -   Ensure both `client/.env` and `server/.env` files exist and are correctly configured.\n    -   Verify the `CLIENT_URL` in `server/.env` matches where your client is running.\n    -   Check the browser console and network tab for specific errors.\n    -   Ensure the backend server is running and accessible at the specified `VITE_BACKEND_ROOT`.\n\n-   **Database connection issues?**\n    -   Verify your PostgreSQL instance is running and accessible.\n    -   Check that the database schema has been properly initialized.\n    -   Ensure the connection parameters in `server/.env` are correct.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatasektionen%2Ffemto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatasektionen%2Ffemto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatasektionen%2Ffemto/lists"}