mirror of
https://github.com/catdevnull/preciazo.git
synced 2024-11-29 21:16:19 +00:00
Compare commits
3 commits
0289313cd6
...
6cb3c8710f
Author | SHA1 | Date | |
---|---|---|---|
6cb3c8710f | |||
fcc7449307 | |||
143c87c433 |
9 changed files with 221 additions and 5 deletions
17
db-datos/drizzle/0004_left_wolfsbane.sql
Normal file
17
db-datos/drizzle/0004_left_wolfsbane.sql
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
-- Custom SQL migration file, put you code below! --
|
||||||
|
create virtual table precios_fts using fts5(ean, url, name, content=precios, content_rowid=id);
|
||||||
|
|
||||||
|
insert into precios_fts(rowid,ean,url,name) select id,ean,url,name from precios;
|
||||||
|
|
||||||
|
-- https://sqlite.org/fts5.html#external_content_and_contentless_tables
|
||||||
|
-- Triggers to keep the FTS index up to date.
|
||||||
|
CREATE TRIGGER precios_fts_ai AFTER INSERT ON precios BEGIN
|
||||||
|
INSERT INTO precios_fts(rowid, ean, url, name) VALUES (new.id, new.ean, new.url, new.name);
|
||||||
|
END;
|
||||||
|
CREATE TRIGGER precios_fts_ad AFTER DELETE ON precios BEGIN
|
||||||
|
INSERT INTO precios_fts(precios_fts, rowid, ean, url, name) VALUES('delete', old.id, old.ean, old.url, old.name);
|
||||||
|
END;
|
||||||
|
CREATE TRIGGER precios_fts_au AFTER UPDATE ON precios BEGIN
|
||||||
|
INSERT INTO precios_fts(precios_fts, rowid, ean, url, name) VALUES('delete', old.id, old.ean, old.url, old.name);
|
||||||
|
INSERT INTO precios_fts(rowid, ean, url, name) VALUES (new.id, new.ean, new.url, new.name);
|
||||||
|
END;
|
101
db-datos/drizzle/meta/0004_snapshot.json
Normal file
101
db-datos/drizzle/meta/0004_snapshot.json
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"id": "bf90a1cd-ae6a-4dba-a1aa-79f14a11d958",
|
||||||
|
"prevId": "e1217fdb-6f54-44c5-a04b-c5aebf202102",
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "sqlite",
|
||||||
|
"tables": {
|
||||||
|
"precios": {
|
||||||
|
"name": "precios",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"ean": {
|
||||||
|
"name": "ean",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fetched_at": {
|
||||||
|
"name": "fetched_at",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"precio_centavos": {
|
||||||
|
"name": "precio_centavos",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"in_stock": {
|
||||||
|
"name": "in_stock",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"name": "url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"warc_record_id": {
|
||||||
|
"name": "warc_record_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"parser_version": {
|
||||||
|
"name": "parser_version",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"image_url": {
|
||||||
|
"name": "image_url",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"precios_ean_idx": {
|
||||||
|
"name": "precios_ean_idx",
|
||||||
|
"columns": [
|
||||||
|
"ean"
|
||||||
|
],
|
||||||
|
"isUnique": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,13 @@
|
||||||
"when": 1703521964385,
|
"when": 1703521964385,
|
||||||
"tag": "0003_abandoned_landau",
|
"tag": "0003_abandoned_landau",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 4,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1703726748364,
|
||||||
|
"tag": "0004_left_wolfsbane",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
FROM oven/bun:1-alpine AS base
|
FROM docker.io/oven/bun:1-alpine AS base
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN bun install --frozen-lockfile \
|
RUN bun install --frozen-lockfile \
|
||||||
&& bun build scraper/cli.ts --target=bun --outfile=/tmp/cli.build.js \
|
&& bun build scraper/cli.ts --target=bun --outfile=/tmp/cli.build.js \
|
||||||
&& rm -rf node_modules/
|
&& rm -rf node_modules/
|
||||||
|
|
||||||
FROM base
|
FROM base
|
||||||
RUN apk add --no-cache wget zstd cronie tini
|
RUN apk add --no-cache wget zstd cronie tini
|
||||||
RUN printf "#!/bin/sh\nexec bun /bin/scraper auto" > /etc/periodic/daily/scraper \
|
RUN printf "#!/bin/sh\nexec bun /bin/scraper auto" > /etc/periodic/daily/scraper \
|
||||||
&& chmod +x /etc/periodic/daily/scraper
|
&& chmod +x /etc/periodic/daily/scraper
|
||||||
|
|
||||||
COPY --from=builder /tmp/cli.build.js /bin/scraper
|
COPY --from=builder /tmp/cli.build.js /bin/scraper
|
||||||
COPY --from=builder /usr/src/app/db-datos/drizzle /bin/drizzle
|
COPY --from=builder /usr/src/app/db-datos/drizzle /bin/drizzle
|
||||||
|
|
|
@ -66,6 +66,8 @@ class Auto {
|
||||||
token: process.env.TELEGRAM_BOT_TOKEN,
|
token: process.env.TELEGRAM_BOT_TOKEN,
|
||||||
chatId: process.env.TELEGRAM_BOT_CHAT_ID,
|
chatId: process.env.TELEGRAM_BOT_CHAT_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.inform("[auto] Empezando scrap");
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadList(supermercado: Supermercado) {
|
async downloadList(supermercado: Supermercado) {
|
||||||
|
|
14
sitio/src/routes/+layout.server.ts
Normal file
14
sitio/src/routes/+layout.server.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { count, countDistinct, eq, max, sql } from "drizzle-orm";
|
||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { db, schema } from "$lib/server/db";
|
||||||
|
const { precios } = schema;
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async () => {
|
||||||
|
const nProductosR = await db
|
||||||
|
.select({
|
||||||
|
count: countDistinct(precios.ean),
|
||||||
|
})
|
||||||
|
.from(precios);
|
||||||
|
const nProductos = nProductosR[0].count;
|
||||||
|
return { nProductos };
|
||||||
|
};
|
|
@ -1,5 +1,43 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import "../app.pcss";
|
import "../app.pcss";
|
||||||
|
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- https://flowbite.com/docs/forms/search-input/ -->
|
||||||
|
<form method="GET" action="/search">
|
||||||
|
<div class="flex items-stretch p-4">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
name="q"
|
||||||
|
class="block w-full rounded-l-lg border border-s-2 border-gray-300 border-s-gray-50 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:border-s-gray-700 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500"
|
||||||
|
placeholder={`Buscar entre ${data.nProductos} productos`}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="block rounded-e-lg border border-blue-700 bg-blue-700 p-2.5 text-sm font-medium text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="h-4 w-4"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Search</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
18
sitio/src/routes/search/+page.server.ts
Normal file
18
sitio/src/routes/search/+page.server.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { error } from "@sveltejs/kit";
|
||||||
|
import { eq, max, sql } from "drizzle-orm";
|
||||||
|
import type { PageServerLoad } from "./$types";
|
||||||
|
import { db, schema } from "$lib/server/db";
|
||||||
|
const { precios } = schema;
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ url }) => {
|
||||||
|
const query = url.searchParams.get("q");
|
||||||
|
let results: null | { ean: string; name: string }[] = null;
|
||||||
|
if (query) {
|
||||||
|
results = db.all(
|
||||||
|
sql`select ean, name from precios_fts where name match ${query};`,
|
||||||
|
);
|
||||||
|
console.debug(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { query, results };
|
||||||
|
};
|
19
sitio/src/routes/search/+page.svelte
Normal file
19
sitio/src/routes/search/+page.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if data.results}
|
||||||
|
<ul>
|
||||||
|
{#each data.results as result}
|
||||||
|
<li>
|
||||||
|
<a href={`/ean/${result.ean}`}>
|
||||||
|
{result.name}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{:else}
|
||||||
|
Probá buscando algo.
|
||||||
|
{/if}
|
Loading…
Reference in a new issue