chore: change endpoint
This commit is contained in:
parent
4f2cdaa302
commit
296083577a
|
@ -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
|
||||||
|
.query({ name: "geolocation" })
|
||||||
|
.then((permissionStatus) => {
|
||||||
if (permissionStatus.state === "granted") {
|
if (permissionStatus.state === "granted") {
|
||||||
setHasPermission(true)
|
setHasPermission(true);
|
||||||
startWatchingLocation()
|
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 Açıldı
|
İçeriğin Kilidi Açı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
1
src/env.d.ts
vendored
|
@ -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" />
|
||||||
|
|
|
@ -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
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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 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,
|
||||||
});
|
});
|
||||||
|
|
|
@ -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 })
|
||||||
|
|
Loading…
Reference in New Issue
Block a user