chore: change endpoint
This commit is contained in:
parent
4f2cdaa302
commit
296083577a
|
@ -1,136 +1,146 @@
|
|||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
|
||||
import { LockClosedIcon, LockOpen1Icon } from "@radix-ui/react-icons"
|
||||
import { useEffect, useState } from "react"
|
||||
import { LockClosedIcon, LockOpen1Icon } from "@radix-ui/react-icons";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import "../styles/locked-content.css"
|
||||
import type { Generated } from "kysely"
|
||||
import { onLocationError } from "@/lib/error"
|
||||
import "../styles/locked-content.css";
|
||||
import type { Generated } from "kysely";
|
||||
import { onLocationError } from "@/lib/error";
|
||||
|
||||
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 = ({
|
||||
contentId = "",
|
||||
imageUrl = "#",
|
||||
location = ""
|
||||
location = "",
|
||||
}: {
|
||||
contentId?: string | Generated<string>
|
||||
imageUrl?: string
|
||||
location?: string
|
||||
contentId?: string | Generated<string>;
|
||||
imageUrl?: string;
|
||||
location?: string;
|
||||
}) => {
|
||||
const [atTarget, setAtTarget] = useState(false)
|
||||
const [contentVisible, setContentVisible] = useState(false)
|
||||
const [hasPermission, setHasPermission] = useState(false)
|
||||
const [watchId, setWatchId] = useState<number>()
|
||||
const [distanceRemain, setDistanceRemain] = useState<string>("")
|
||||
const [atTarget, setAtTarget] = useState(false);
|
||||
const [contentVisible, setContentVisible] = useState(false);
|
||||
const [hasPermission, setHasPermission] = useState(false);
|
||||
const [watchId, setWatchId] = useState<number>();
|
||||
const [distanceRemain, setDistanceRemain] = useState<string>("");
|
||||
|
||||
const targetCoordinates = JSON.parse(location).coordinates
|
||||
const targetCoordinates = JSON.parse(location);
|
||||
|
||||
console.log("coor", targetCoordinates);
|
||||
|
||||
const targetPos = {
|
||||
lat: targetCoordinates[0],
|
||||
lng: targetCoordinates[1]
|
||||
}
|
||||
lng: targetCoordinates[1],
|
||||
};
|
||||
|
||||
const startWatchingLocation = () => {
|
||||
setHasPermission(true)
|
||||
setHasPermission(true);
|
||||
if (!watchId) {
|
||||
const id = navigator.geolocation.watchPosition(
|
||||
(position: GeolocationPosition) => {
|
||||
const pos = {
|
||||
lat: position.coords.latitude,
|
||||
lng: position.coords.longitude
|
||||
}
|
||||
lng: position.coords.longitude,
|
||||
};
|
||||
|
||||
// @ts-expect-error 3rd party script
|
||||
const targetLatLng = L.latLng(targetPos)
|
||||
const targetLatLng = L.latLng(targetPos);
|
||||
|
||||
// @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) {
|
||||
setDistanceRemain(`${(betweenMeters / 1000).toFixed()} KM`)
|
||||
setDistanceRemain(`${(betweenMeters / 1000).toFixed()} KM`);
|
||||
} else if (betweenMeters > 50) {
|
||||
setDistanceRemain(`${betweenMeters.toFixed(0)} M`)
|
||||
setDistanceRemain(`${betweenMeters.toFixed(0)} M`);
|
||||
} else {
|
||||
setAtTarget(true)
|
||||
setAtTarget(true);
|
||||
}
|
||||
},
|
||||
err => onLocationError(err),
|
||||
(err) => onLocationError(err),
|
||||
{ enableHighAccuracy: true, timeout: 27000, maximumAge: 10000 }
|
||||
)
|
||||
);
|
||||
|
||||
setWatchId(id)
|
||||
}
|
||||
setWatchId(id);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUnlock = async () => {
|
||||
setContentVisible(true)
|
||||
await incrementCounter(contentId)
|
||||
}
|
||||
setContentVisible(true);
|
||||
await incrementCounter(contentId);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
navigator.permissions.query({ name: "geolocation" }).then(permissionStatus => {
|
||||
navigator.permissions
|
||||
.query({ name: "geolocation" })
|
||||
.then((permissionStatus) => {
|
||||
if (permissionStatus.state === "granted") {
|
||||
setHasPermission(true)
|
||||
startWatchingLocation()
|
||||
setHasPermission(true);
|
||||
startWatchingLocation();
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (contentVisible) {
|
||||
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} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
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 ? (
|
||||
<div className='flex flex-col justify-center items-center image-wrapper'>
|
||||
<img src={imageUrl} className='blur-2xl h-[450px]' />
|
||||
<div className="flex flex-col justify-center items-center image-wrapper">
|
||||
<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-lg p-6 animate-pulse bg-indigo-600 hover:bg-indigo-700 hover:animate-none border-2 border-indigo-800'
|
||||
onClick={handleUnlock}>
|
||||
<LockOpen1Icon className='mr-2 h-4 w-4' />
|
||||
size="lg"
|
||||
className="text-lg p-6 animate-pulse bg-indigo-600 hover:bg-indigo-700 hover:animate-none border-2 border-indigo-800"
|
||||
onClick={handleUnlock}
|
||||
>
|
||||
<LockOpen1Icon className="mr-2 h-4 w-4" />
|
||||
İçeriğin Kilidi Açıldı
|
||||
</Button>
|
||||
|
||||
<Card className='p-2'>
|
||||
<CardContent className='pb-0 text-center'>İçeriği görmek için butona bas!</CardContent>
|
||||
<Card className="p-2">
|
||||
<CardContent className="pb-0 text-center">
|
||||
İçeriği görmek için butona bas!
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex flex-col justify-center items-center image-wrapper'>
|
||||
<img src={imageUrl} className='blur-2xl h-[450px]' />
|
||||
<div className='flex flex-col justify-center gap-4 overlay'>
|
||||
<Button size='lg' className='text-md'>
|
||||
<LockClosedIcon className='mr-2 h-4 w-4' /> İçerik Kilitli
|
||||
<div className="flex flex-col justify-center items-center image-wrapper">
|
||||
<img src={imageUrl} className="blur-2xl h-[450px]" />
|
||||
<div className="flex flex-col justify-center gap-4 overlay">
|
||||
<Button size="lg" className="text-md">
|
||||
<LockClosedIcon className="mr-2 h-4 w-4" /> İçerik Kilitli
|
||||
</Button>
|
||||
<Card className='p-2'>
|
||||
<Card className="p-2">
|
||||
{hasPermission ? (
|
||||
<CardContent className='pb-0 text-center'>
|
||||
<CardContent className="pb-0 text-center">
|
||||
<p>İçeriği görmek için konuma gitmelisin!</p>
|
||||
<p>{distanceRemain && `Kalan mesafe: ${distanceRemain}`}</p>
|
||||
</CardContent>
|
||||
) : (
|
||||
<div className='flex flex-col gap-2'>
|
||||
<CardContent className='pb-0 text-center'>
|
||||
<div className="flex flex-col gap-2">
|
||||
<CardContent className="pb-0 text-center">
|
||||
Ne kadar yaklaştığını görmek için aşağıdaki butona bas.
|
||||
</CardContent>
|
||||
|
||||
<Button
|
||||
size='sm'
|
||||
className='bg-green-700 hover:bg-green-600 text-md'
|
||||
onClick={() => startWatchingLocation()}>
|
||||
size="sm"
|
||||
className="bg-green-700 hover:bg-green-600 text-md"
|
||||
onClick={() => startWatchingLocation()}
|
||||
>
|
||||
Konum İzni Ver
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -140,8 +150,8 @@ const LocationButton = ({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default LocationButton
|
||||
export default LocationButton;
|
||||
|
|
1
src/env.d.ts
vendored
1
src/env.d.ts
vendored
|
@ -1,3 +1,4 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
/// <reference types="@types/google.maps" />
|
||||
/// <reference types="@types/leaflet" />
|
||||
|
|
|
@ -19,16 +19,18 @@ import dayjs from 'dayjs';
|
|||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
|
||||
type Content = Omit<ContentTable, 'url'>;
|
||||
type Content = ContentTable;
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
||||
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;
|
||||
|
||||
console.log(data)
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
@ -68,8 +70,8 @@ const dateFromNow = dayjs.utc(data?.created_at).from(dayjs.utc());
|
|||
</Card>
|
||||
|
||||
<LockedContent
|
||||
contentId={data?.id}
|
||||
imageUrl={data?.blob_url}
|
||||
contentId={data?.url}
|
||||
imageUrl={`http://localhost:3000/images/${data?.blob_url}`}
|
||||
location={data?.loc}
|
||||
client:load
|
||||
/>
|
||||
|
|
|
@ -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"
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
})
|
||||
}
|
|
@ -195,7 +195,7 @@ import { Label } from '@/components/ui/label';
|
|||
|
||||
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',
|
||||
body: formData,
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const data = JSON.parse(document.getElementById('map').dataset.targetLocation)
|
||||
|
||||
const TARGET_LOCATION = data.coordinates
|
||||
const TARGET_LOCATION = data
|
||||
|
||||
function startWatchingLocation() {
|
||||
map.locate({ watch: true })
|
||||
|
|
Loading…
Reference in New Issue
Block a user