feat: fetch location and content data from database
This commit is contained in:
parent
38dd1eb2f1
commit
6e42f5d55e
|
@ -9,6 +9,6 @@ export default defineConfig({
|
|||
integrations: [react(), tailwind({
|
||||
applyBaseStyles: false
|
||||
})],
|
||||
output: "hybrid",
|
||||
output: "server",
|
||||
adapter: vercel()
|
||||
});
|
||||
|
|
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -25,6 +25,7 @@
|
|||
"clsx": "^2.1.0",
|
||||
"kysely": "^0.26.0",
|
||||
"lucide-react": "^0.309.0",
|
||||
"nanoid": "^5.0.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
|
@ -5003,9 +5004,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.4.tgz",
|
||||
"integrity": "sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
@ -5013,10 +5014,10 @@
|
|||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
"nanoid": "bin/nanoid.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
"node": "^18 || >=20"
|
||||
}
|
||||
},
|
||||
"node_modules/napi-build-utils": {
|
||||
|
@ -5689,6 +5690,23 @@
|
|||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||
},
|
||||
"node_modules/postcss/node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
"clsx": "^2.1.0",
|
||||
"kysely": "^0.26.0",
|
||||
"lucide-react": "^0.309.0",
|
||||
"nanoid": "^5.0.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useEffect, useState } from "react"
|
|||
|
||||
import "../styles/locked-content.css"
|
||||
|
||||
const LocationButton = () => {
|
||||
const LocationButton = ({ imageUrl = "#" }: { imageUrl?: string }) => {
|
||||
const [atTarget, setAtTarget] = useState(false)
|
||||
const [contentVisible, setContentVisible] = useState(false)
|
||||
const [hasPermission, setHasPermission] = useState(false)
|
||||
|
@ -47,29 +47,36 @@ const LocationButton = () => {
|
|||
}, [])
|
||||
|
||||
if (contentVisible) {
|
||||
return <div className='module-unlocked w-full h-[450px] p-4'></div>
|
||||
return (
|
||||
<div className='w-full h-[475px] p-4 flex justify-center'>
|
||||
<img src={imageUrl} />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className='module w-full h-[450px] p-4'>
|
||||
<div className='w-full h-[475px] p-4'>
|
||||
{atTarget ? (
|
||||
<div className='module-inside flex flex-col justify-center items-center gap-2'>
|
||||
<div>
|
||||
<div className='flex flex-col justify-center items-center image-wrapper'>
|
||||
<img src={imageUrl} className='blur-lg h-[450px]' />
|
||||
|
||||
<div className='flex flex-col justify-center gap-2 overlay'>
|
||||
<Button size={"lg"} onClick={() => setContentVisible(true)}>
|
||||
<LockOpen1Icon className='mr-2 h-4 w-4' />
|
||||
İçeriğin Kilidi Açıldı
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card className='p-2'>
|
||||
<CardContent className='pb-0 text-center'>İçeriği görmek için butona bas!</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className='module-inside flex flex-col justify-center items-center gap-4'>
|
||||
<Button size={"lg"}>
|
||||
<div className='flex flex-col justify-center items-center image-wrapper'>
|
||||
<img src={imageUrl} className='blur-lg h-[450px]' />
|
||||
<div className='flex flex-col justify-center gap-2 overlay'>
|
||||
<Button size='lg'>
|
||||
<LockClosedIcon className='mr-2 h-4 w-4' /> İçerik Kilitli
|
||||
</Button>
|
||||
|
||||
<Card className='p-2'>
|
||||
{hasPermission ? (
|
||||
<CardContent className='pb-0 text-center'>İçeriği görmek için konuma gitmelisin!</CardContent>
|
||||
|
@ -90,6 +97,7 @@ const LocationButton = () => {
|
|||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
|
13
src/lib/db.ts
Normal file
13
src/lib/db.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import type { Generated } from "kysely"
|
||||
|
||||
export interface Database {
|
||||
contents: ContentTable
|
||||
}
|
||||
|
||||
export interface ContentTable {
|
||||
id: Generated<string>
|
||||
url: string
|
||||
blob_url: string
|
||||
loc: string
|
||||
description: string
|
||||
}
|
|
@ -15,6 +15,14 @@ import { CalendarIcon } from '@radix-ui/react-icons';
|
|||
import '../styles/locked-page.css';
|
||||
import LockedContent from '@/components/LockedContent';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
||||
const res = await fetch(`http://localhost:4321/api/content?id=${id}`);
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
console.log(data);
|
||||
---
|
||||
|
||||
<Layout>
|
||||
|
@ -39,10 +47,7 @@ import { Separator } from '@/components/ui/separator';
|
|||
<CardTitle>Abdurrahman</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p>
|
||||
Hacı sana çok özel bir sürpriz hazırladık, aşağıdaki konuma muhakak
|
||||
git.
|
||||
</p>
|
||||
<p>{data.description}</p>
|
||||
</CardContent>
|
||||
<CardFooter className="gap-2">
|
||||
<CalendarIcon />
|
||||
|
@ -50,9 +55,14 @@ import { Separator } from '@/components/ui/separator';
|
|||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<LockedContent client:load />
|
||||
<LockedContent imageUrl={data.blob_url} client:load />
|
||||
|
||||
<div id="map" class="w-full h-[450px] rounded"></div>
|
||||
<div
|
||||
id="map"
|
||||
class="w-full h-[450px] rounded"
|
||||
data-target-location={data.loc}
|
||||
>
|
||||
</div>
|
||||
|
||||
<Button className="w-full">Paylaş</Button>
|
||||
<div class="flex justify-center">
|
|
@ -1,20 +1,9 @@
|
|||
export const prerender = false
|
||||
|
||||
import { put } from "@vercel/blob"
|
||||
import type { Generated } from "kysely"
|
||||
import type { APIRoute } from "astro"
|
||||
import { createKysely } from "@vercel/postgres-kysely"
|
||||
import { customAlphabet } from "nanoid"
|
||||
|
||||
interface ContentTable {
|
||||
id: Generated<number>
|
||||
blob_url: string
|
||||
loc: string
|
||||
description: string
|
||||
}
|
||||
|
||||
interface Database {
|
||||
contents: ContentTable
|
||||
}
|
||||
import type { Database } from "../../lib/db"
|
||||
|
||||
export const POST: APIRoute = async ({ request }) => {
|
||||
const data = await request.formData()
|
||||
|
@ -43,9 +32,14 @@ export const POST: APIRoute = async ({ request }) => {
|
|||
|
||||
const db = createKysely<Database>({ connectionString: import.meta.env.POSTGRES_URL })
|
||||
|
||||
const nanoid = customAlphabet("abcdefghijklmnoprstuvyz", 10)
|
||||
|
||||
const newUrl = nanoid()
|
||||
|
||||
const res = await db
|
||||
.insertInto("contents")
|
||||
.values({
|
||||
url: `${newUrl.slice(0, 3)}-${newUrl.slice(3, 7)}-${newUrl.slice(7)}`,
|
||||
blob_url: blob.url,
|
||||
description: description?.toString() ?? "",
|
||||
loc: `SRID=4326;POINT(${pos[0]} ${pos[1]})`
|
||||
|
@ -66,3 +60,39 @@ export const POST: APIRoute = async ({ request }) => {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 }) => ["blob_url", fn<string>("ST_AsGeoJSON", ["loc"]).as("loc"), "description"])
|
||||
.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,4 +1,6 @@
|
|||
---
|
||||
export const prerender = true;
|
||||
|
||||
import '@/styles/globals.css';
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
@ -109,7 +111,7 @@ import { Textarea } from '@/components/ui/textarea';
|
|||
|
||||
const formData = new FormData(e.target as HTMLFormElement);
|
||||
|
||||
const response = await fetch(`/create-url`, {
|
||||
const response = await fetch(`/api/content`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const TARGET_LOCATION = [41.01907795861253, 29.01715377829709];
|
||||
const data = JSON.parse(document.getElementById('map').dataset.targetLocation)
|
||||
|
||||
const TARGET_LOCATION = data.coordinates
|
||||
|
||||
var map = L.map('map').setView(TARGET_LOCATION, 13);
|
||||
|
||||
|
|
|
@ -1,42 +1,14 @@
|
|||
.module {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.module-unlocked {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.module::before {
|
||||
content: "";
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.overlay {
|
||||
position: absolute;
|
||||
background-image: url("/sample-selfie.webp");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
filter: blur(20px);
|
||||
}
|
||||
|
||||
.module-unlocked::before {
|
||||
content: "";
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
background-image: url("/sample-selfie.webp");
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
/* center overlay text */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.module-inside {
|
||||
position: relative;
|
||||
inset: 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user