Skip to content

Commit 1e9f98e

Browse files
committed
Initial Commit
1 parent b12e839 commit 1e9f98e

File tree

15 files changed

+330
-0
lines changed

15 files changed

+330
-0
lines changed

.github/workflows/main.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Update README Version Badge
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- "package.json"
9+
10+
jobs:
11+
update-packages:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v3
16+
17+
- name: Setup Node.js
18+
uses: actions/setup-node@v3
19+
with:
20+
node-version: "20"
21+
22+
- name: Update README Version Badge
23+
run: |
24+
VERSION=$(node -p "require('./package.json').version")
25+
sed -i "s/Version [0-9]*\.[0-9]*\.[0-9]*/Version $VERSION/g" README.md
26+
sed -i "s/badge\/Version-[0-9]*\.[0-9]*\.[0-9]*-/badge\/Version-$VERSION-/g" README.md
27+
28+
- name: Commit changes
29+
run: |
30+
git config --global user.name 'GitHub Actions Bot'
31+
git config --global user.email 'actions@github.com'
32+
git add package.json package-lock.json README.md
33+
git commit -m "Update NPM packages and README version badge" || echo "No changes to commit"
34+
git push origin HEAD:${{ github.ref }}
35+
env:
36+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.node-version

Whitespace-only changes.

netlify.toml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[build]
2+
publish = "public"
3+
functions = "netlify/functions"
4+
5+
[build.environment]
6+
NODE_VERSION = "20"
7+
8+
[[edge_functions]]
9+
path = "/shorten"
10+
function = "shorten"
11+
12+
[[edge_functions]]
13+
path = "/count"
14+
function = "count"
15+
16+
[[edge_functions]]
17+
path = "/latest"
18+
function = "latest"
19+
20+
[[edge_functions]]
21+
path = "/:shortUrl"
22+
function = "redirect"

netlify/edge-functions/count.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { headers } from "./headers.ts";
2+
import { fetchFromSupabase } from "./utils.ts";
3+
4+
export default async (request: Request): Promise<Response> => {
5+
try {
6+
const data = await fetchFromSupabase("urls?select=id", { method: "GET" });
7+
const count = data.length;
8+
return new Response(JSON.stringify({ count }), { status: 200, headers });
9+
} catch (err) {
10+
return new Response(
11+
JSON.stringify({ error: "Internal server error", details: err.message }),
12+
{ status: 500, headers }
13+
);
14+
}
15+
};

netlify/edge-functions/headers.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const headers = {
2+
"Access-Control-Allow-Origin": "*",
3+
"Access-Control-Allow-Methods": "*",
4+
"Access-Control-Allow-Headers": "*",
5+
"Access-Control-Max-Age": "86400",
6+
"Content-Type": "application/json",
7+
};

netlify/edge-functions/latest.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { headers } from "./headers.ts";
2+
import { fetchFromSupabase } from "./utils.ts";
3+
4+
export default async (request: Request): Promise<Response> => {
5+
try {
6+
const url = new URL(request.url);
7+
const count = url.searchParams.get("count") || "10";
8+
const data = await fetchFromSupabase(
9+
`urls?order=created_at.desc&limit=${count}`,
10+
{ method: "GET" }
11+
);
12+
return new Response(JSON.stringify(data), { status: 200, headers });
13+
} catch (err) {
14+
return new Response(
15+
JSON.stringify({ error: "Internal server error", details: err.message }),
16+
{ status: 500, headers }
17+
);
18+
}
19+
};

netlify/edge-functions/redirect.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { fetchFromSupabase } from "./utils.ts";
2+
3+
export default async (request: Request): Promise<Response> => {
4+
try {
5+
const shortUrl = new URL(request.url).pathname.replace("/", "");
6+
const data = await fetchFromSupabase(
7+
`urls?select=long_url&short_url=eq.${shortUrl}`,
8+
{ method: "GET" }
9+
);
10+
if (data.length === 0) {
11+
return new Response(JSON.stringify({ error: "URL not found" }), {
12+
status: 404,
13+
});
14+
}
15+
return new Response(null, {
16+
status: 301,
17+
headers: { Location: data[0].long_url },
18+
});
19+
} catch (err) {
20+
return new Response(
21+
JSON.stringify({ error: "Internal server error", details: err.message }),
22+
{ status: 500 }
23+
);
24+
}
25+
};

netlify/edge-functions/shorten.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { multiParser } from "https://deno.land/x/multiparser@0.114.0/mod.ts";
2+
import { headers } from "./headers.ts";
3+
import { fetchFromSupabase, generateShortUrl } from "./utils.ts";
4+
5+
export default async (request: Request): Promise<Response> => {
6+
try {
7+
const form = await multiParser(request);
8+
const url = form.fields.url;
9+
10+
if (!url) {
11+
return new Response(JSON.stringify({ error: "No URL provided" }), {
12+
status: 400,
13+
headers,
14+
});
15+
}
16+
17+
const shortUrl = generateShortUrl();
18+
await fetchFromSupabase("urls", {
19+
method: "POST",
20+
headers: { "Content-Type": "application/json" },
21+
body: JSON.stringify({ short_url: shortUrl, long_url: url }),
22+
});
23+
24+
return new Response(JSON.stringify({ shortUrl }), { status: 200, headers });
25+
} catch (err) {
26+
return new Response(
27+
JSON.stringify({ error: "Internal server error", details: err.message }),
28+
{ status: 500, headers }
29+
);
30+
}
31+
};

netlify/edge-functions/utils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
const SUPABASE_URL = Deno.env.get("SUPABASE_URL");
2+
const SUPABASE_KEY = Deno.env.get("SUPABASE_ANON_KEY");
3+
const SUPABASE_TABLE = "urls";
4+
5+
export async function fetchFromSupabase(
6+
endpoint: string,
7+
options: RequestInit
8+
) {
9+
const response = await fetch(`${SUPABASE_URL}/rest/v1/${endpoint}`, {
10+
...options,
11+
headers: {
12+
...options.headers,
13+
apikey: SUPABASE_KEY,
14+
Authorization: `Bearer ${SUPABASE_KEY}`,
15+
},
16+
});
17+
return await response.json();
18+
}
19+
20+
export function generateShortUrl(): string {
21+
return crypto.randomUUID().substring(0, 7);
22+
}

package-lock.json

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)