mirror of https://github.com/TZGyn/shortener
added qr color customization
parent
9cb2243c00
commit
f5f911c30e
@ -0,0 +1,5 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS "setting" (
|
||||||
|
"user_id" integer NOT NULL,
|
||||||
|
"qr_background" varchar(7),
|
||||||
|
"qr_foreground" varchar(7)
|
||||||
|
);
|
||||||
@ -0,0 +1,256 @@
|
|||||||
|
{
|
||||||
|
"id": "6fa088cf-5f05-40aa-bb38-94848c4feb92",
|
||||||
|
"prevId": "9a3733db-b0ef-4744-b0bf-f69e9b03917c",
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "pg",
|
||||||
|
"tables": {
|
||||||
|
"project": {
|
||||||
|
"name": "project",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"name": "uuid",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"session": {
|
||||||
|
"name": "session",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"token": {
|
||||||
|
"name": "token",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expires": {
|
||||||
|
"name": "expires",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"setting": {
|
||||||
|
"name": "setting",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"qr_background": {
|
||||||
|
"name": "qr_background",
|
||||||
|
"type": "varchar(7)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"qr_foreground": {
|
||||||
|
"name": "qr_foreground",
|
||||||
|
"type": "varchar(7)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"shortener": {
|
||||||
|
"name": "shortener",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"name": "link",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"name": "code",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"name": "project_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"name": "user",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"name": "uuid",
|
||||||
|
"type": "uuid",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "gen_random_uuid()"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"name": "password",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"user_email_unique": {
|
||||||
|
"name": "user_email_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"visitor": {
|
||||||
|
"name": "visitor",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "serial",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"shortener_id": {
|
||||||
|
"name": "shortener_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "now()"
|
||||||
|
},
|
||||||
|
"country_code": {
|
||||||
|
"name": "country_code",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"country": {
|
||||||
|
"name": "country",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"city": {
|
||||||
|
"name": "city",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { redirect } from '@sveltejs/kit'
|
||||||
|
import type { PageServerLoad } from './$types'
|
||||||
|
|
||||||
|
export const load = (async () => {
|
||||||
|
throw redirect(300, '/settings/account')
|
||||||
|
}) satisfies PageServerLoad
|
||||||
@ -1,15 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { PageData } from './$types'
|
|
||||||
import { Separator } from '$lib/components/ui/separator'
|
|
||||||
export let data: PageData
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="space-y-6">
|
|
||||||
<div>
|
|
||||||
<h3 class="text-lg font-medium">Profile</h3>
|
|
||||||
<p class="text-muted-foreground text-sm">
|
|
||||||
This is how others will see you on the site.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
|
||||||
</div>
|
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import { default as QrCode } from 'qrious'
|
||||||
|
|
||||||
|
export let errorCorrection = 'L'
|
||||||
|
export let background = '#fff'
|
||||||
|
export let color = '#000'
|
||||||
|
export let size = '300'
|
||||||
|
export let value = 'example.com/abcdefgh'
|
||||||
|
export let padding = 10
|
||||||
|
export let className = 'qrcode'
|
||||||
|
|
||||||
|
let image = ''
|
||||||
|
|
||||||
|
function generateQrCode() {
|
||||||
|
if (!document || !window) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const QRcode = new QrCode()
|
||||||
|
QRcode.set({
|
||||||
|
background,
|
||||||
|
foreground: color,
|
||||||
|
level: errorCorrection,
|
||||||
|
padding,
|
||||||
|
size,
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
|
||||||
|
image = QRcode.toDataURL()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
generateQrCode()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img src={image} alt={value} class={className} />
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { db } from '$lib/db'
|
||||||
|
import type { PageServerLoad } from './$types'
|
||||||
|
|
||||||
|
export const load = (async (event) => {
|
||||||
|
const settings = await db.query.setting.findFirst({
|
||||||
|
where: (setting, { eq }) =>
|
||||||
|
eq(setting.userId, event.locals.userObject.id),
|
||||||
|
})
|
||||||
|
return { settings }
|
||||||
|
}) satisfies PageServerLoad
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Separator } from '$lib/components/ui/separator'
|
||||||
|
import { Input } from '$lib/components/ui/input'
|
||||||
|
import { Label } from '$lib/components/ui/label'
|
||||||
|
import type { PageData } from './$types'
|
||||||
|
import Button from '$lib/components/ui/button/button.svelte'
|
||||||
|
import { Loader2 } from 'lucide-svelte'
|
||||||
|
import { invalidateAll } from '$app/navigation'
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
|
import DemoQr from './(components)/DemoQR.svelte'
|
||||||
|
|
||||||
|
export let data: PageData
|
||||||
|
|
||||||
|
let isLoading = false
|
||||||
|
|
||||||
|
let qr_data = {
|
||||||
|
background_color: data.settings?.qr_background || '#fff',
|
||||||
|
foreground_color: data.settings?.qr_foreground || '#000',
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(qr_data)
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/account/qr', {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify({
|
||||||
|
qr_background: qr_data.background_color,
|
||||||
|
qr_foreground: qr_data.foreground_color,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const body = await response.json()
|
||||||
|
|
||||||
|
if (body.success) {
|
||||||
|
toast.success('Account Details Updated')
|
||||||
|
await invalidateAll()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.error('Error Occurred')
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-medium">Account</h3>
|
||||||
|
<p class="text-muted-foreground text-sm">
|
||||||
|
Update your account settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
{#key qr_data}
|
||||||
|
<DemoQr
|
||||||
|
background={qr_data.background_color}
|
||||||
|
color={qr_data.foreground_color} />
|
||||||
|
{/key}
|
||||||
|
|
||||||
|
<div class="flex w-full max-w-sm flex-col gap-2">
|
||||||
|
<Label for="background_color">Background Color</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id="background_color"
|
||||||
|
placeholder="#ffffff"
|
||||||
|
bind:value={qr_data.background_color} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex w-full max-w-sm flex-col gap-2">
|
||||||
|
<Label for="foreground_color">Foreground Color</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
id="foreground_color"
|
||||||
|
placeholder="#000000"
|
||||||
|
bind:value={qr_data.foreground_color} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button disabled={isLoading} on:click={submit} class="flex gap-2">
|
||||||
|
{#if isLoading}
|
||||||
|
<Loader2 class="animate-spin" />
|
||||||
|
{/if}
|
||||||
|
Save</Button>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { db } from '$lib/db'
|
||||||
|
import { setting } from '$lib/db/schema'
|
||||||
|
import { qrUpdateSchema } from '$lib/server/types'
|
||||||
|
import { eq } from 'drizzle-orm'
|
||||||
|
import type { RequestHandler } from './$types'
|
||||||
|
|
||||||
|
export const PUT: RequestHandler = async (event) => {
|
||||||
|
const body = await event.request.json()
|
||||||
|
const qrUpdate = qrUpdateSchema.safeParse(body)
|
||||||
|
const userId = event.locals.userObject.id
|
||||||
|
|
||||||
|
if (!qrUpdate.success) {
|
||||||
|
return new Response(JSON.stringify({ success: false }))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const settings = await db.query.setting.findFirst({
|
||||||
|
where: (settingData, { eq }) => eq(settingData.userId, userId),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!settings) {
|
||||||
|
await db.insert(setting).values({ userId })
|
||||||
|
}
|
||||||
|
await db
|
||||||
|
.update(setting)
|
||||||
|
.set(qrUpdate.data)
|
||||||
|
.where(eq(setting.userId, userId))
|
||||||
|
return new Response(JSON.stringify({ success: true }))
|
||||||
|
} catch (error) {
|
||||||
|
return new Response(JSON.stringify({ success: false }))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue