{"id":32481132,"url":"https://github.com/forattini-dev/fshortner","last_synced_at":"2025-10-27T02:25:48.275Z","repository":{"id":258134727,"uuid":"866293014","full_name":"forattini-dev/fshortner","owner":"forattini-dev","description":"fshortner","archived":false,"fork":false,"pushed_at":"2024-10-16T04:56:21.000Z","size":299,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-17T19:57:28.597Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/forattini-dev.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-10-02T01:35:57.000Z","updated_at":"2024-10-16T04:56:25.000Z","dependencies_parsed_at":"2024-10-20T19:32:24.601Z","dependency_job_id":null,"html_url":"https://github.com/forattini-dev/fshortner","commit_stats":null,"previous_names":["forattini-dev/fshortner"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/forattini-dev/fshortner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forattini-dev%2Ffshortner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forattini-dev%2Ffshortner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forattini-dev%2Ffshortner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forattini-dev%2Ffshortner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/forattini-dev","download_url":"https://codeload.github.com/forattini-dev/fshortner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forattini-dev%2Ffshortner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281202665,"owners_count":26460667,"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-10-27T02:00:05.855Z","response_time":61,"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":"2025-10-27T02:25:45.239Z","updated_at":"2025-10-27T02:25:48.268Z","avatar_url":"https://github.com/forattini-dev.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Fshortner\n\nThis is a simple lightweight URL shortning service using express and s3db.js.\n\nFork-it and use it as you like.\n\n\n## Start the service\n\n```bash\ndocker run --rm -itd \\\n  -p 8000:8000 \\\n  -e PORT=8000 \\\n  -e FS_CONNECTION_STRING=\"your-secret\" \\\n  --name fshortner \\\n  ghcr.io/forattini-dev/fshortner:latest\n```\n\n## Service global configuration\n\n| variable | description | default |\n| :--- | --- | :---: |\n| PORT | Port to run the service. | `8000` |\n| FS_ID_SIZE | Size of the ID to generate. | `16` |\n| FS_SESSION_SECRET | Secret to sign the session. | `secret` |\n| FS_DOMAIN | Domain to use in the shareable link. | inferred |\n| FS_BEHIND_PROXY | Enable it if the service is behind a proxy. | `false` |\n| FS_REDIRECT_TEMPLATE | Interface's template. See `./src/views` dir for more. | `corporate` |\n| FS_REDIRECT_TIMEOUT | Timeout to redirect to the original URL. | `0.6` |\n| FS_CONNECTION_STRING | Secret to connect to the s3db.js database. | `null` |\n| FS_COSTS_ENABLED | Enable the costs plugin. | `true` |\n| FS_CRON_ENABLED | Enable the cron jobs. | `true` |\n| FS_CRON_CLICKS_COUNTER | Cron expression to update the clicks counter. | `*/30 * * * * *` |\n| FS_CRON_VIEWS_COUNTER | Cron expression to update the views counter. | `*/30 * * * * *` |\n| FS_AUTH_ENABLED | Enable the basic authentication middleware. | `false` |\n| FS_AUTH_USERNAME | Username to authenticate. | `fshortner` |\n| FS_AUTH_PASSWORD | Password to authenticate. | `secret` |\n\n\n## Authentication\n\nThe service uses a basic authentication middleware to protect the endpoints.\n\nFirst, enable the `FS_AUTH_ENABLED` variable.\n\nTo authenticate, use the `FS_AUTH_USERNAME` and `FS_AUTH_PASSWORD` variables.\n\n### Example with curl\n\n```bash\ncurl -u fshortner:secret http://localhost:8000/v1/urls\n```\n\n## Example with axios\n\n```js\nimport axios from 'axios'\n\nconst response = await axios.get('https://example.com/endpoint', {\n  auth: {\n    username: 'fshortner',\n    password: 'secret'\n  }\n});\n```\n\n## Example of creating a client with ky\n\n```js\nimport axios from 'ky'\n\nconst client = await ky.create({\n  prefixUrl: 'https://example.com',\n  headers: {\n    Authorization: `Basic ${Buffer.from(`fshortner:secret`).toString('base64')}`\n  }\n});\n```\n\n## API specs\n\n### Shorten a URL\n\n`POST /v1/urls`\n\n```js\n// request: \n{\n  \"link\": \"https://my-super-loooooooooooong-url.com?p=with+parameters\"\n}\n\n// response:\n{\n  \"id\": \"AdCcOPzRR4UXCMs4\",\n  \"link\": \"https://my-super-loooooooooooong-url.com?p=with+parameters\",\n  \"shareableLink\": \"http://localhost:8000/AdCcOPzRR4UXCMs4\"\n}\n```\n\nBody parameters:\n| parameter | description | required | type | default |\n| :--- | --- | :---: | :---: | :---: |\n| link | URL to shorten. | true | url string | null |\n| webhook | URL to send updates. | false | url string | null |\n| getFingerprints | Enable to get the fingerprints. | true | boolean | true |\n\n#### Ex: shorten a URL and get instant updates\n\n```js\n// request \n{\n  \"link\": \"https://my-super-loooooooooooong-url.com?p=with+parameters\",\n  \"webhook\": \"https://my-webhook.io/?id=my-token\"\n}\n\n// response:\n{\n  \"id\": \"AdCcOPzRR4UXCMs4\",\n  \"link\": \"https://my-super-loooooooooooong-url.com?p=with+parameters\",\n  \"shareableLink\": \"http://localhost:8000/AdCcOPzRR4UXCMs4\",\n  \"webhook\": \"https://my-webhook.io/?id=my-token\"\n}\n```\n\n### Get status of a URL\n\n`GET /v1/urls/:id`\n\n```js\n// response:\n{\n  \"id\": \"AdCcOPzRR4UXCMs4\",\n  \"clicks\": 121,\n  \"link\": \"https://my-super-loooooooooooong-url.com?p=with+parameters\",\n  \"shareableLink\": \"http://localhost:8000/AdCcOPzRR4UXCMs4\",\n  \"webhook\": true,\n  \"createdAt\": \"2024-10-07T02:25:35.000Z\"\n}\n``` \n\n### Get a QR code of a URL\n\n`GET /v1/urls/:id/qrcode`\n\nResponse is a image/png.\n\n\n\n## Contributing\n\n1. Fork it! :)\n1. Run it with `docker-compose up`\n1. At your minio container, create credentials and use the example at `./src/concerns/minio-policy.json` to create a policy.\n1. Make your requests to `http://localhost:8000`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforattini-dev%2Ffshortner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fforattini-dev%2Ffshortner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforattini-dev%2Ffshortner/lists"}