chore: change endpoint

This commit is contained in:
log101 2024-07-05 18:01:22 +03:00
parent 4f2cdaa302
commit 296083577a
9 changed files with 89 additions and 267 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -1,136 +1,146 @@
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card" import { Card, CardContent } from "@/components/ui/card";
import { LockClosedIcon, LockOpen1Icon } from "@radix-ui/react-icons" import { LockClosedIcon, LockOpen1Icon } from "@radix-ui/react-icons";
import { useEffect, useState } from "react" import { useEffect, useState } from "react";
import "../styles/locked-content.css" import "../styles/locked-content.css";
import type { Generated } from "kysely" import type { Generated } from "kysely";
import { onLocationError } from "@/lib/error" import { onLocationError } from "@/lib/error";
const incrementCounter = async (id: string | Generated<string>) => const incrementCounter = async (id: string | Generated<string>) =>
await fetch(`${import.meta.env.PUBLIC_HOME_URL}/api/content/increment?id=${id}`) await fetch(`http://localhost:3000/api/location/increment/${id}`, {
method: "PATCH",
});
const LocationButton = ({ const LocationButton = ({
contentId = "", contentId = "",
imageUrl = "#", imageUrl = "#",
location = "" location = "",
}: { }: {
contentId?: string | Generated<string> contentId?: string | Generated<string>;
imageUrl?: string imageUrl?: string;
location?: string location?: string;
}) => { }) => {
const [atTarget, setAtTarget] = useState(false) const [atTarget, setAtTarget] = useState(false);
const [contentVisible, setContentVisible] = useState(false) const [contentVisible, setContentVisible] = useState(false);
const [hasPermission, setHasPermission] = useState(false) const [hasPermission, setHasPermission] = useState(false);
const [watchId, setWatchId] = useState<number>() const [watchId, setWatchId] = useState<number>();
const [distanceRemain, setDistanceRemain] = useState<string>("") const [distanceRemain, setDistanceRemain] = useState<string>("");
const targetCoordinates = JSON.parse(location).coordinates const targetCoordinates = JSON.parse(location);
console.log("coor", targetCoordinates);
const targetPos = { const targetPos = {
lat: targetCoordinates[0], lat: targetCoordinates[0],
lng: targetCoordinates[1] lng: targetCoordinates[1],
} };
const startWatchingLocation = () => { const startWatchingLocation = () => {
setHasPermission(true) setHasPermission(true);
if (!watchId) { if (!watchId) {
const id = navigator.geolocation.watchPosition( const id = navigator.geolocation.watchPosition(
(position: GeolocationPosition) => { (position: GeolocationPosition) => {
const pos = { const pos = {
lat: position.coords.latitude, lat: position.coords.latitude,
lng: position.coords.longitude lng: position.coords.longitude,
} };
// @ts-expect-error 3rd party script // @ts-expect-error 3rd party script
const targetLatLng = L.latLng(targetPos) const targetLatLng = L.latLng(targetPos);
// @ts-expect-error 3rd party script // @ts-expect-error 3rd party script
const currentLatLng = L.latLng(pos) const currentLatLng = L.latLng(pos);
const betweenMeters = currentLatLng.distanceTo(targetLatLng) const betweenMeters = currentLatLng.distanceTo(targetLatLng);
if (betweenMeters > 1000) { if (betweenMeters > 1000) {
setDistanceRemain(`${(betweenMeters / 1000).toFixed()} KM`) setDistanceRemain(`${(betweenMeters / 1000).toFixed()} KM`);
} else if (betweenMeters > 50) { } else if (betweenMeters > 50) {
setDistanceRemain(`${betweenMeters.toFixed(0)} M`) setDistanceRemain(`${betweenMeters.toFixed(0)} M`);
} else { } else {
setAtTarget(true) setAtTarget(true);
} }
}, },
err => onLocationError(err), (err) => onLocationError(err),
{ enableHighAccuracy: true, timeout: 27000, maximumAge: 10000 } { enableHighAccuracy: true, timeout: 27000, maximumAge: 10000 }
) );
setWatchId(id) setWatchId(id);
} }
} };
const handleUnlock = async () => { const handleUnlock = async () => {
setContentVisible(true) setContentVisible(true);
await incrementCounter(contentId) await incrementCounter(contentId);
} };
useEffect(() => { useEffect(() => {
navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => { navigator.permissions
if (permissionStatus.state === "granted") { .query({ name: "geolocation" })
setHasPermission(true) .then((permissionStatus) => {
startWatchingLocation() if (permissionStatus.state === "granted") {
} setHasPermission(true);
}) startWatchingLocation();
}, []) }
});
}, []);
if (contentVisible) { if (contentVisible) {
return ( return (
<div className='w-full h-[475px] p-4 flex justify-center'> <div className="w-full h-[475px] p-4 flex justify-center">
<img src={imageUrl} /> <img src={imageUrl} />
</div> </div>
) );
} else { } else {
return ( return (
<div className='w-full h-[475px] overflow-hidden border border-zinc-200 shadow-sm p-4 rounded'> <div className="w-full h-[475px] overflow-hidden border border-zinc-200 shadow-sm p-4 rounded">
{atTarget ? ( {atTarget ? (
<div className='flex flex-col justify-center items-center image-wrapper'> <div className="flex flex-col justify-center items-center image-wrapper">
<img src={imageUrl} className='blur-2xl h-[450px]' /> <img src={imageUrl} className="blur-2xl h-[450px]" />
<div className='flex flex-col justify-center gap-4 overlay'> <div className="flex flex-col justify-center gap-4 overlay">
<Button <Button
size='lg' size="lg"
className='text-lg p-6 animate-pulse bg-indigo-600 hover:bg-indigo-700 hover:animate-none border-2 border-indigo-800' className="text-lg p-6 animate-pulse bg-indigo-600 hover:bg-indigo-700 hover:animate-none border-2 border-indigo-800"
onClick={handleUnlock}> onClick={handleUnlock}
<LockOpen1Icon className='mr-2 h-4 w-4' /> >
<LockOpen1Icon className="mr-2 h-4 w-4" />
İçeriğin Kilidi ıldı İçeriğin Kilidi ıldı
</Button> </Button>
<Card className='p-2'> <Card className="p-2">
<CardContent className='pb-0 text-center'>İçeriği görmek için butona bas!</CardContent> <CardContent className="pb-0 text-center">
İçeriği görmek için butona bas!
</CardContent>
</Card> </Card>
</div> </div>
</div> </div>
) : ( ) : (
<div className='flex flex-col justify-center items-center image-wrapper'> <div className="flex flex-col justify-center items-center image-wrapper">
<img src={imageUrl} className='blur-2xl h-[450px]' /> <img src={imageUrl} className="blur-2xl h-[450px]" />
<div className='flex flex-col justify-center gap-4 overlay'> <div className="flex flex-col justify-center gap-4 overlay">
<Button size='lg' className='text-md'> <Button size="lg" className="text-md">
<LockClosedIcon className='mr-2 h-4 w-4' /> İçerik Kilitli <LockClosedIcon className="mr-2 h-4 w-4" /> İçerik Kilitli
</Button> </Button>
<Card className='p-2'> <Card className="p-2">
{hasPermission ? ( {hasPermission ? (
<CardContent className='pb-0 text-center'> <CardContent className="pb-0 text-center">
<p>İçeriği görmek için konuma gitmelisin!</p> <p>İçeriği görmek için konuma gitmelisin!</p>
<p>{distanceRemain && `Kalan mesafe: ${distanceRemain}`}</p> <p>{distanceRemain && `Kalan mesafe: ${distanceRemain}`}</p>
</CardContent> </CardContent>
) : ( ) : (
<div className='flex flex-col gap-2'> <div className="flex flex-col gap-2">
<CardContent className='pb-0 text-center'> <CardContent className="pb-0 text-center">
Ne kadar yaklaştığını görmek için aşağıdaki butona bas. Ne kadar yaklaştığını görmek için aşağıdaki butona bas.
</CardContent> </CardContent>
<Button <Button
size='sm' size="sm"
className='bg-green-700 hover:bg-green-600 text-md' className="bg-green-700 hover:bg-green-600 text-md"
onClick={() => startWatchingLocation()}> onClick={() => startWatchingLocation()}
>
Konum İzni Ver Konum İzni Ver
</Button> </Button>
</div> </div>
@ -140,8 +150,8 @@ const LocationButton = ({
</div> </div>
)} )}
</div> </div>
) );
} }
} };
export default LocationButton export default LocationButton;

1
src/env.d.ts vendored
View File

@ -1,3 +1,4 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" /> /// <reference types="astro/client" />
/// <reference types="@types/google.maps" /> /// <reference types="@types/google.maps" />
/// <reference types="@types/leaflet" /> /// <reference types="@types/leaflet" />

View File

@ -19,16 +19,18 @@ import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime'; import relativeTime from 'dayjs/plugin/relativeTime';
import utc from 'dayjs/plugin/utc'; import utc from 'dayjs/plugin/utc';
type Content = Omit<ContentTable, 'url'>; type Content = ContentTable;
const { id } = Astro.params; const { id } = Astro.params;
const res = await fetch( const res = await fetch(
`${import.meta.env.PUBLIC_HOME_URL}/api/content?id=${id}` `http://localhost:3000/api/location/${id}`
); );
const data: Content | null = res.status === 200 ? await res.json() : null; const data: Content | null = res.status === 200 ? await res.json() : null;
console.log(data)
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
dayjs.extend(utc); dayjs.extend(utc);
@ -68,8 +70,8 @@ const dateFromNow = dayjs.utc(data?.created_at).from(dayjs.utc());
</Card> </Card>
<LockedContent <LockedContent
contentId={data?.id} contentId={data?.url}
imageUrl={data?.blob_url} imageUrl={`http://localhost:3000/images/${data?.blob_url}`}
location={data?.loc} location={data?.loc}
client:load client:load
/> />

View File

@ -1,39 +0,0 @@
import type { Database } from "@/lib/db"
import { createKysely } from "@vercel/postgres-kysely"
import type { APIRoute } from "astro"
export const GET: APIRoute = async ({ request }) => {
const contentId = new URL(request.url).searchParams.get("id")
if (!contentId) {
return new Response(null, {
status: 400,
statusText: "Content id is required"
})
}
const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL })
try {
const result = await db
.updateTable("contents")
.set(eb => ({ unlocked_counter: eb("unlocked_counter", "+", 1) }))
.where("id", "=", contentId)
.executeTakeFirst()
if (result) {
return new Response(JSON.stringify({ counter: Number(result) }))
} else {
return new Response(null, {
status: 404,
statusText: "Could not increment the counter"
})
}
} catch (error) {
console.error("Error fetching content:", error)
return new Response(null, {
status: 500,
statusText: "Error while fetching content"
})
}
}

View File

@ -1,141 +0,0 @@
import { createClient } from "@supabase/supabase-js"
import type { APIRoute } from "astro"
import { createKysely } from "@vercel/postgres-kysely"
import { customAlphabet } from "nanoid"
import sharpService from "astro/assets/services/sharp"
import type { Database } from "@/lib/db"
export const POST: APIRoute = async ({ request }) => {
const formData = await request.formData()
const image = formData.get("selected-photo") as File
const author = formData.get("author")
const description = formData.get("description")
const geolocation = formData.get("geolocation")
if (!image || !geolocation) {
return new Response(null, {
status: 400,
statusText: "Image and geolocation are required fields"
})
}
const pos = geolocation.toString().split(",")
if (pos.length !== 2) {
return new Response(null, {
status: 400,
statusText: "Geolocation not correctly formatted"
})
}
const supabaseUrl = import.meta.env.SUPABASE_URL
const supabaseKey = import.meta.env.SUPABASE_KEY
const supabase = createClient(supabaseUrl, supabaseKey)
const nanoid = customAlphabet("abcdefghijklmnoprstuvyz", 10)
const randomImageId = nanoid()
const imageName = `${image.name.replace(/\.[^/.]+$/, "")}${randomImageId}.webp`
const imageBuf = await image.arrayBuffer()
const { data, format } = await sharpService.transform(
new Uint8Array(imageBuf),
{ src: imageName, format: "webp" },
{ domains: [], remotePatterns: [], service: { entrypoint: "", config: { limitInputPixels: false } } }
)
console.log(format)
const { error } = await supabase.storage.from("images").upload(`public/${imageName}`, data, {
cacheControl: "3600",
upsert: false,
contentType: "image/webp"
})
if (error) {
console.error(error.message, imageName, error.cause)
return new Response(null, {
status: 400,
statusText: error.message
})
}
const imagePublicUrl = `https://sozfqjbdyppxfwhqktja.supabase.co/storage/v1/object/public/images/public/${imageName}`
const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL })
const newUrl = nanoid()
const res = await db
.insertInto("contents")
.values({
url: `${newUrl.slice(0, 3)}-${newUrl.slice(3, 7)}-${newUrl.slice(7)}`,
blob_url: imagePublicUrl,
author: author?.toString() ?? "",
description: description?.toString() ?? "",
loc: `SRID=4326;POINT(${pos[0]} ${pos[1]})`
})
.returning("url")
.executeTakeFirst()
if (res?.url) {
return new Response(
JSON.stringify({
url: res.url
})
)
} else {
return new Response(null, {
status: 500,
statusText: "Error while saving data"
})
}
}
export const GET: APIRoute = async ({ request }) => {
const contentId = new URL(request.url).searchParams.get("id")
if (!contentId) {
return new Response(null, {
status: 400,
statusText: "Content id is required"
})
}
const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL })
try {
const content = await db
.selectFrom("contents")
.select(({ fn }) => [
"id",
"blob_url",
fn<string>("ST_AsGeoJSON", ["loc"]).as("loc"),
"description",
"author",
"created_at",
"unlocked_counter"
])
.where("url", "=", contentId)
.executeTakeFirst()
if (content) {
return new Response(JSON.stringify(content))
} else {
return new Response(null, {
status: 404,
statusText: "Content not found"
})
}
} catch (error) {
console.error("Error fetching content:", error)
return new Response(null, {
status: 500,
statusText: "Error while fetching content"
})
}
}

View File

@ -1,11 +0,0 @@
import type { APIRoute } from "astro"
export const POST: APIRoute = async ({ request }) => {
const data = await request.json()
console.log(data)
return new Response(null, {
status: 200
})
}

View File

@ -195,7 +195,7 @@ import { Label } from '@/components/ui/label';
const formData = new FormData(e.target as HTMLFormElement); const formData = new FormData(e.target as HTMLFormElement);
const res = await fetch(`/api/content`, { const res = await fetch(`http://127.0.0.1:3000/api/location`, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}); });

View File

@ -1,6 +1,6 @@
const data = JSON.parse(document.getElementById('map').dataset.targetLocation) const data = JSON.parse(document.getElementById('map').dataset.targetLocation)
const TARGET_LOCATION = data.coordinates const TARGET_LOCATION = data
function startWatchingLocation() { function startWatchingLocation() {
map.locate({ watch: true }) map.locate({ watch: true })