add search
This commit is contained in:
parent
ae4895fff3
commit
b393f16432
2
.gitignore
vendored
2
.gitignore
vendored
@ -0,0 +1,2 @@
|
||||
.env
|
||||
http_ca.crt
|
60
api/repo.ts
60
api/repo.ts
@ -1,3 +1,15 @@
|
||||
import { Client as ElasticsearchClient, SearchHit } from "https://deno.land/x/elasticsearch@v8.3.3/mod.ts";
|
||||
import { config } from "https://deno.land/std@0.166.0/dotenv/mod.ts";
|
||||
import { Doc } from "../doc_load/load.ts";
|
||||
|
||||
const env = await config({ export: true });
|
||||
const client = new ElasticsearchClient({
|
||||
node: "https://localhost:9200",
|
||||
auth: {
|
||||
username: env["ELASTIC_USERNAME"],
|
||||
password: env["ELASTIC_PASSWORD"],
|
||||
}
|
||||
});
|
||||
|
||||
export interface RepoData {
|
||||
name: string;
|
||||
@ -10,6 +22,19 @@ export interface RepoData {
|
||||
readme: string;
|
||||
}
|
||||
|
||||
function docToRepoData(doc: Doc): RepoData {
|
||||
return {
|
||||
name: doc.name,
|
||||
author: doc.author,
|
||||
description: doc.desc,
|
||||
url: doc.url,
|
||||
stars: doc.star,
|
||||
forks: doc.fork,
|
||||
tags: doc.tags,
|
||||
readme: doc.readme,
|
||||
};
|
||||
}
|
||||
|
||||
export const SAMPLE_DATA: RepoData[] = [
|
||||
{
|
||||
name: "deno",
|
||||
@ -50,8 +75,18 @@ export const SAMPLE_DATA: RepoData[] = [
|
||||
]
|
||||
|
||||
export const getRepos = async (): Promise<RepoData[]> => {
|
||||
// return mock data for now
|
||||
return SAMPLE_DATA;
|
||||
const res = await client.search<Doc>({
|
||||
target: "github-awesome",
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
sort: [
|
||||
{ "star": "desc" },
|
||||
]
|
||||
},
|
||||
})
|
||||
return res.hits.hits.map((hit: SearchHit<Doc>) => docToRepoData(hit._source));
|
||||
}
|
||||
|
||||
export interface SearchOptions {
|
||||
@ -70,6 +105,23 @@ export interface SearchOptions {
|
||||
}
|
||||
|
||||
export async function searchRepos(query: string, options?: SearchOptions): Promise<RepoData[]> {
|
||||
// return mock data for now
|
||||
return SAMPLE_DATA;
|
||||
const res = await client.search<Doc>({
|
||||
target: "github-awesome",
|
||||
body: {
|
||||
query: {
|
||||
multi_match: {
|
||||
query,
|
||||
fields: ["name", "desc", "readme", "tags", "author"],
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
"_score",
|
||||
{ "star": "desc" },
|
||||
],
|
||||
from: options?.offset,
|
||||
size: options?.limit,
|
||||
},
|
||||
});
|
||||
//console.log(res);
|
||||
return res.hits.hits.map((hit: SearchHit<Doc>) => docToRepoData(hit._source));
|
||||
}
|
207
cli.ts
207
cli.ts
@ -1,8 +1,203 @@
|
||||
import {join} from 'https://deno.land/std@0.166.0/path/mod.ts';
|
||||
import { expandGlob } from 'https://deno.land/std@0.166.0/fs/mod.ts';
|
||||
import { DocParser, readDoc } from './doc_load/load.ts';
|
||||
import { expandGlob } from 'std/fs/mod.ts';
|
||||
import { config } from "std/dotenv/mod.ts";
|
||||
import { chunk } from "https://deno.land/std/collections/chunk.ts"
|
||||
import { Client as ElasticsearchClient } from "https://deno.land/x/elasticsearch@v8.3.3/mod.ts";
|
||||
import { Doc, DocParser, readDoc } from './doc_load/load.ts';
|
||||
import ProgressBar from 'https://deno.land/x/progress@v1.3.0/mod.ts';
|
||||
import { Command } from "cliffy";
|
||||
|
||||
for await(const dir of expandGlob('sample/*.md')) {
|
||||
console.log(dir.path);
|
||||
await readDoc(dir.path, true);
|
||||
|
||||
const env = await config({ export: true });
|
||||
const client = new ElasticsearchClient({
|
||||
node: 'https://localhost:9200',
|
||||
auth: {
|
||||
username: env['ELASTIC_USERNAME'],
|
||||
password: env['ELASTIC_PASSWORD'],
|
||||
}
|
||||
});
|
||||
|
||||
async function createIndex() {
|
||||
const res = await client.indices.create({
|
||||
index: 'github-awesome',
|
||||
body: {
|
||||
mappings: {
|
||||
properties: {
|
||||
"name": {
|
||||
type: "text",
|
||||
},
|
||||
"desc": {
|
||||
type: "text",
|
||||
},
|
||||
"url": {
|
||||
type: "keyword",
|
||||
},
|
||||
"star": {
|
||||
type: "integer",
|
||||
},
|
||||
"fork": {
|
||||
type: "integer",
|
||||
},
|
||||
"author": {
|
||||
type: "keyword",
|
||||
},
|
||||
"tags": {
|
||||
type: "keyword",
|
||||
"ignore_above": 256,
|
||||
},
|
||||
"readme": {
|
||||
type: "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
console.log(res);
|
||||
console.log(res.acknowledged ? 'Index created' : 'Index creation failed');
|
||||
}
|
||||
|
||||
async function deleteIndex() {
|
||||
const res = await client.indices.delete({
|
||||
index: 'github-awesome',
|
||||
});
|
||||
console.log(res);
|
||||
console.log(res.acknowledged ? 'Index deleted' : 'Index deletion failed');
|
||||
}
|
||||
|
||||
async function bulkIndex(path: string[],{
|
||||
chunkSize = 1000,
|
||||
progressBar = false,
|
||||
}) {
|
||||
const ch = chunk(path, chunkSize);
|
||||
const bar = new ProgressBar({
|
||||
total: ch.length,
|
||||
title: 'Indexing',
|
||||
width: 50,
|
||||
});
|
||||
let i = 0;
|
||||
for (const pathes of ch) {
|
||||
const docs = await Promise.all(pathes.map(async (path) => {
|
||||
const doc = await readDoc(path);
|
||||
if (doc.from_url){
|
||||
delete doc.from_url;
|
||||
}
|
||||
return [
|
||||
{
|
||||
create: {
|
||||
_id: doc.author+"/"+doc.name,
|
||||
}
|
||||
},
|
||||
doc
|
||||
] as [{ create: { _id: string } }, Doc];
|
||||
}
|
||||
));
|
||||
const _ = await client.documents.bulk({
|
||||
target: 'github-awesome',
|
||||
body: docs.flat(),
|
||||
});
|
||||
|
||||
if (progressBar) {
|
||||
bar.render(++i);
|
||||
}
|
||||
}
|
||||
if (progressBar) {
|
||||
bar.end();
|
||||
}
|
||||
}
|
||||
|
||||
async function test_search(query: string, {
|
||||
size = 10,
|
||||
from = 0,
|
||||
}) {
|
||||
const res = await client.search<Doc>({
|
||||
target: 'github-awesome',
|
||||
body: {
|
||||
query: {
|
||||
multi_match: {
|
||||
query,
|
||||
fields: ['name', 'desc', 'tags', 'author', 'readme'],
|
||||
}
|
||||
},
|
||||
from,
|
||||
size,
|
||||
},
|
||||
});
|
||||
return res.hits.hits;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const cmd = new Command();
|
||||
cmd
|
||||
.name('github-awesome')
|
||||
.version('0.1.0')
|
||||
.description('github-awesome search engine cli');
|
||||
cmd
|
||||
.command('index [path...]')
|
||||
.description('index github-awesome. glob pattern is supported.')
|
||||
.option('-c, --chunk-size <chunkSize:number>', 'chunk size', {
|
||||
default: 200,
|
||||
})
|
||||
.option('-p, --progress-bar', 'show progress bar')
|
||||
.action(async ({chunkSize, progressBar}, ...path: string[]) => {
|
||||
const pathes = [];
|
||||
for (const p of path) {
|
||||
for await (const iterator of expandGlob(p)) {
|
||||
pathes.push(iterator.path);
|
||||
}
|
||||
}
|
||||
if (pathes.length === 0) {
|
||||
console.log('no path found');
|
||||
return;
|
||||
}
|
||||
await bulkIndex(pathes, {
|
||||
chunkSize,
|
||||
progressBar
|
||||
});
|
||||
});
|
||||
cmd
|
||||
.command('search <query>')
|
||||
.description('search github-awesome')
|
||||
.option('-s, --size <size:number>', 'size', {
|
||||
default: 10,
|
||||
})
|
||||
.option('-f, --from <from:number>', 'from', {
|
||||
default: 0,
|
||||
})
|
||||
.option('-j, --json', 'output json')
|
||||
.action(async ({size, from, json}, query: string) => {
|
||||
const s = await test_search(query, {
|
||||
size,
|
||||
from,
|
||||
});
|
||||
if (s.length === 0) {
|
||||
console.log('no result found');
|
||||
return;
|
||||
}
|
||||
if (json) {
|
||||
console.log(JSON.stringify(s, null, 2));
|
||||
}
|
||||
else {
|
||||
for (const doc of s) {
|
||||
console.log("id :",doc._id);
|
||||
console.log("score :",doc._score);
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
});
|
||||
cmd
|
||||
.command('create-index')
|
||||
.description('create index')
|
||||
.action(async () => {
|
||||
await createIndex();
|
||||
});
|
||||
cmd
|
||||
.command('delete-index')
|
||||
.description('delete index')
|
||||
.action(async () => {
|
||||
await deleteIndex();
|
||||
});
|
||||
await cmd.parse(Deno.args);
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
await main();
|
||||
}
|
@ -3,25 +3,29 @@ import { useState } from "preact/hooks";
|
||||
export interface SearchBarProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onSubmit: () => void;
|
||||
}
|
||||
|
||||
export function SearchBar(props: SearchBarProps) {
|
||||
const { value, onChange } = props;
|
||||
const [inputValue, setInputValue] = useState(value);
|
||||
const { value, onChange, onSubmit } = props;
|
||||
|
||||
return (
|
||||
<div class="flex items-center justify-center w-full p-4">
|
||||
<input class="w-full flex-auto p-2 border border-gray-200 rounded
|
||||
focus:outline-none focus:border-gray-600"
|
||||
type="text" placeholder="Search..." value={inputValue}
|
||||
type="text" placeholder="Search..." value={value}
|
||||
onInput={e=>{
|
||||
if(e.currentTarget){
|
||||
setInputValue(e.currentTarget.value);
|
||||
onChange(e.currentTarget.value);
|
||||
}
|
||||
}}/>
|
||||
}}
|
||||
onKeyUp={e=>{
|
||||
if(e.key === "Enter"){
|
||||
onSubmit();
|
||||
}
|
||||
}}/>
|
||||
<button class="flex-grow-0 p-2 ml-2 text-white bg-blue-500 rounded hover:bg-blue-600"
|
||||
type="submit">Submit</button>
|
||||
type="submit" onClick={onSubmit}>Submit</button>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
{
|
||||
"tasks": {
|
||||
"start": "deno run -A --watch=static/,routes/ dev.ts"
|
||||
"start": "deno run -A --cert http_ca.crt --watch=static/,routes/ dev.ts",
|
||||
"cli": "deno run -A --unstable --cert http_ca.crt cli.ts",
|
||||
"validate": "deno run -A validator.ts"
|
||||
},
|
||||
"importMap": "./import_map.json",
|
||||
"compilerOptions": {
|
||||
|
65
deno.lock
65
deno.lock
@ -60,6 +60,7 @@
|
||||
"https://deno.land/std@0.152.0/async/mux_async_iterator.ts": "5b4aca6781ad0f2e19ccdf1d1a1c092ccd3e00d52050d9c27c772658c8367256",
|
||||
"https://deno.land/std@0.152.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239",
|
||||
"https://deno.land/std@0.152.0/async/tee.ts": "bcfae0017ebb718cf4eef9e2420e8675d91cb1bcc0ed9b668681af6e6caad846",
|
||||
"https://deno.land/std@0.152.0/encoding/base64.ts": "c57868ca7fa2fbe919f57f88a623ad34e3d970d675bdc1ff3a9d02bba7409db2",
|
||||
"https://deno.land/std@0.152.0/http/server.ts": "0b0a9f3abfcfecead944b31ee9098a0c11a59b0495bf873ee200eb80e7441483",
|
||||
"https://deno.land/std@0.161.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
|
||||
"https://deno.land/std@0.161.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934",
|
||||
@ -87,6 +88,54 @@
|
||||
"https://deno.land/std@0.166.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4",
|
||||
"https://deno.land/std@0.166.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a",
|
||||
"https://deno.land/std@0.166.0/bytes/mod.ts": "b2e342fd3669176a27a4e15061e9d588b89c1aaf5008ab71766e23669565d179",
|
||||
"https://deno.land/std@0.166.0/collections/_comparators.ts": "b9edf2170aaccbe11407d37e8948b6867cf68fbe5225f4cd4cdb02f174227157",
|
||||
"https://deno.land/std@0.166.0/collections/_utils.ts": "fd759867be7a0047a1fa89ec89f7b58ebe3f2f7f089a8f4e416eb30c5d764868",
|
||||
"https://deno.land/std@0.166.0/collections/aggregate_groups.ts": "c2932492fce4a117b0a271082cecea193b1c3fd7db2422484e67a3c2b8872868",
|
||||
"https://deno.land/std@0.166.0/collections/associate_by.ts": "272dc97bd296dbf53ba19acc95fda2a8fa8883ed7929ad0b597cfc7efd87c106",
|
||||
"https://deno.land/std@0.166.0/collections/associate_with.ts": "2206bbc2f497768a0d73e3de08fa8a7a3d2b12e2d731ffa2b2fb7727a5d86909",
|
||||
"https://deno.land/std@0.166.0/collections/binary_heap.ts": "1879bec8df29e85615789e40b4991dbc944c3d25b5df6f416df1406c6bbffbe0",
|
||||
"https://deno.land/std@0.166.0/collections/chunk.ts": "6713208d07b9fa3535e46f6aa2c06a57fe0e7497cf7703b0808d85e56ce1c487",
|
||||
"https://deno.land/std@0.166.0/collections/deep_merge.ts": "a4252c99f82fe4051c6dfbe0c8ba839888c4233ab99c556ba519c5290011c281",
|
||||
"https://deno.land/std@0.166.0/collections/distinct.ts": "6440d486163278e5b81d55299d83d6706f690b1c929165cc2c673ecd245df851",
|
||||
"https://deno.land/std@0.166.0/collections/distinct_by.ts": "52ab6146482825932f53f7da2bdd7ae3ac566d38bf1d028edbaf109a4013329e",
|
||||
"https://deno.land/std@0.166.0/collections/drop_last_while.ts": "36f01ebc2c73d1eb5bba60e76c41101a19f379ffb80b21a313a47b57e99a97d9",
|
||||
"https://deno.land/std@0.166.0/collections/drop_while.ts": "9ab60aee3956028efedb72bad6c821765bd615aebc49fe874c9f8ab82844f5ad",
|
||||
"https://deno.land/std@0.166.0/collections/filter_entries.ts": "e3995d73926835a244af192aaa9b7bb3c11641d0efb801807808e4919d281a28",
|
||||
"https://deno.land/std@0.166.0/collections/filter_keys.ts": "fd0099e0dbf2cad8e52b441a4dac3f7f46adabea3279caf89eb4ed3408cb0f96",
|
||||
"https://deno.land/std@0.166.0/collections/filter_values.ts": "faf87e3c28a8042f4e4d83c0c65cea71de2d9311644114e93b7f5c93b10cda1a",
|
||||
"https://deno.land/std@0.166.0/collections/find_single.ts": "36bd5bb4c5b5b77310dbb4795ad7a88d66efb7fbf5f839c219dc766325ba56ba",
|
||||
"https://deno.land/std@0.166.0/collections/first_not_nullish_of.ts": "7e41ff961587c00132ee2c580b4a0a2b15e0f3eb57f281738997a69c0628281b",
|
||||
"https://deno.land/std@0.166.0/collections/group_by.ts": "3cf14e55c99320fca7ce6c1521d44170ab4a62c87738937a506357b234145f11",
|
||||
"https://deno.land/std@0.166.0/collections/includes_value.ts": "cceda098877f99912c152cec9e0a495f44063f009c9fdb2d00b97d3c307218dd",
|
||||
"https://deno.land/std@0.166.0/collections/intersect.ts": "d9fbee487b6c5690ad72d1555d10892367028754bae6c57b9471e6b23377e0c6",
|
||||
"https://deno.land/std@0.166.0/collections/join_to_string.ts": "24e35e1a7898047aa7277fdde4526e01102d25b1c47bc5a8c6b7d37a8a83d4a0",
|
||||
"https://deno.land/std@0.166.0/collections/map_entries.ts": "f0978e222dec4e4fb9d177115f19c0f09f229f952ff897433067e95fbf3c1fb7",
|
||||
"https://deno.land/std@0.166.0/collections/map_keys.ts": "2139fe25f35a6ef2b91bb00c9cd8b5e9ff2def5a2f714c57bc31c3a45d5aa041",
|
||||
"https://deno.land/std@0.166.0/collections/map_not_nullish.ts": "3585509bb9fe9cdc6843a51ece02be972a6630922dcda0bbe24e387ec85e21dc",
|
||||
"https://deno.land/std@0.166.0/collections/map_values.ts": "7e73685397409f2a1bc5356d89a58ce0249faf9e38db29434a8733144c877a2f",
|
||||
"https://deno.land/std@0.166.0/collections/max_by.ts": "bdc89ab14345aa3e332d87caf5e0f5b9b9f7840bd41addbfa59ba3f00ec158ec",
|
||||
"https://deno.land/std@0.166.0/collections/max_of.ts": "61808e8b030ba64fc703a89959d50f607554f7739e0ccfe96c4c46646d651a30",
|
||||
"https://deno.land/std@0.166.0/collections/max_with.ts": "5adbde35bf0f4636d544d4e54ebcf5af48374ccd3a64ad26affb003621801adc",
|
||||
"https://deno.land/std@0.166.0/collections/min_by.ts": "4fb3ca15babdc354cfb194374db3bb2ef58dccb83eba81ea2fee40dffd32c58f",
|
||||
"https://deno.land/std@0.166.0/collections/min_of.ts": "8435f5f6add95bf2fc91ba229cb8e44a1fdac9d1974bd25527b83e5276fb56d3",
|
||||
"https://deno.land/std@0.166.0/collections/min_with.ts": "c3e81382f8eabd81d8bb728bd9ba843c159eef8130561a3a8e595fd26d84d7cf",
|
||||
"https://deno.land/std@0.166.0/collections/mod.ts": "35b55ac18219107ffcf74051e7989611536d3fb96173f5053df935fdc32cefed",
|
||||
"https://deno.land/std@0.166.0/collections/partition.ts": "dab859fc9d359a54e4f3ae491dbe51b7299ff0d32a53460fd0b48d03ed10de80",
|
||||
"https://deno.land/std@0.166.0/collections/permutations.ts": "86475866d36016d15aae7b9c560be163b43879b6aa0b6ef412f6091783c68d51",
|
||||
"https://deno.land/std@0.166.0/collections/reduce_groups.ts": "29417b912316e06bda8f2ae57d262e4ba18af076c33fedf3290c7bb5d1507f14",
|
||||
"https://deno.land/std@0.166.0/collections/running_reduce.ts": "e2f21013ea13f04d2faab4258f2b6004153f420c8680cb03b56637b56e6eda1d",
|
||||
"https://deno.land/std@0.166.0/collections/sample.ts": "f3cb000285da721952bf1c79c5baaa613d742b19bf1a738767a41312be6ddb25",
|
||||
"https://deno.land/std@0.166.0/collections/sliding_windows.ts": "b386957c2ee81111c316f90585f71faee893952cd5f9426db60f15934ddf6659",
|
||||
"https://deno.land/std@0.166.0/collections/sort_by.ts": "224fb6bc59f940c6521f06cd81b5f5a5eb887e86e9078070d14299a0847614f4",
|
||||
"https://deno.land/std@0.166.0/collections/sum_of.ts": "106a416e4169a78f0e8e6dc5c71f25b3b96cafb3f8713f36737cba6c4008580d",
|
||||
"https://deno.land/std@0.166.0/collections/take_last_while.ts": "17c57d73397819458f0e6c969a2044d16cd89cb7ecc2c7bb1015ab465f74f1fd",
|
||||
"https://deno.land/std@0.166.0/collections/take_while.ts": "b66dfb3d9e9c16f3973c5824ee7f04107eb3251f4ec7a5f6b0e26d672ee592bd",
|
||||
"https://deno.land/std@0.166.0/collections/union.ts": "436587bd092d9675bcf9fc8c6c4b82e10920b2127e2107b9b810dc594e2d9164",
|
||||
"https://deno.land/std@0.166.0/collections/unzip.ts": "bfc58ee369b48e14c3de74c35f32be2ae255c0ef26dba10da1ec192f93428ee4",
|
||||
"https://deno.land/std@0.166.0/collections/without_all.ts": "f18b9a5e3fe3bbb4f65e92ca41ca0ade6975f44c0afbe6cf0e66d56fbe193249",
|
||||
"https://deno.land/std@0.166.0/collections/zip.ts": "a04a97f62ae7020329d2f56794ecc333930c6834b4bb1f0e7dfbf75777210e3e",
|
||||
"https://deno.land/std@0.166.0/dotenv/mod.ts": "b149416f0daa0361873097495d16adbb321b8bcb594dcc5cdb6bf9639fd173fd",
|
||||
"https://deno.land/std@0.166.0/dotenv/util.ts": "6cc392f087577a26a27f0463f77cc0c31a390aa055917099935b36eb2454592d",
|
||||
"https://deno.land/std@0.166.0/encoding/_yaml/dumper/dumper.ts": "5bd334372608a1aec7a2343705930889d4048f57a2c4d398f1d6d75996ecd0d3",
|
||||
"https://deno.land/std@0.166.0/encoding/_yaml/dumper/dumper_state.ts": "3c1bc8519c1832f0f136856881b97f0b42f64b7968767dbc36b8b0b6cae963dc",
|
||||
"https://deno.land/std@0.166.0/encoding/_yaml/error.ts": "6ca899f6d86c6979bce6d7c3a6a8e2a360b09d8b0f55d2e649bd1233604fb7c9",
|
||||
@ -252,6 +301,19 @@
|
||||
"https://deno.land/x/denomander@0.9.3/src/utils/remove.ts": "e80f1d257f76cbcafafc855debe7274c59db319639b51aca63a63003e8cf1118",
|
||||
"https://deno.land/x/denomander@0.9.3/src/utils/set.ts": "a89fe0f27575cecd5f5fdaa6907f13a387a03c83b3ef66fd317d114f4dc0fe3e",
|
||||
"https://deno.land/x/denomander@0.9.3/src/utils/utils.ts": "fc29c3b267065685c45d24b3e597e67bee94b2b9d68b5739625051358fef541e",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/deps.ts": "18920291f3b1d48f1a10d462b5b4ab79e20aa630c43814c25cff028935018fa2",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/mod.ts": "c012c590c515ed56078bb1583e4488b5dffa7b7efe41900b4f9407787de18807",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/client.ts": "9396ca210678ce39f8a3a10a1667bfd82948fd00cad937906907f74a21a46f42",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/helpers/mod.ts": "520d8cca6906cdd34f795b57b1975a5bb729c8937a0cf1e7be142686075c2bb6",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/helpers/request.ts": "6ba039199f9958ca1b972f08517fd963987cdbb94bddf0fe8bd6125feb0b9b75",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/helpers/serializer.ts": "d1a402ca489335c439fe9d5b58d3c0879cb993b02ef86416a0c398a1cad38bda",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/rest/cat.ts": "d3f31d96951ac8ebf538a9d60b71c4d133c38c273d3ab56e1e3da882b8d86590",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/rest/cluster.ts": "afbde8f1b49f95d18e785978bf105a9f2e53e0d3834a1e9d589bbb77bb37f1c8",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/rest/documents.ts": "069704e06db6736c6b29a51dc25ec05bca71bd7f3d4bb0f52390eb92714ca3f9",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/rest/indices.ts": "8678c1cbffbb54c336f266fd6ff5a1a9b078cfd43938fa2a1fda003af2f0c372",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/rest/rest.ts": "983a0b0f8456d1e751d4bd27284d9a85060beccc4e8300f100a08f30d72530b8",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/rest/sql.ts": "218e3f6a7780ff773dbcb9c1cc67ae02a52eea5b1bf1e2a6a06b41feb68974ca",
|
||||
"https://deno.land/x/elasticsearch@v8.3.3/src/types.d.ts": "2e79495e7097d9da14ae284b7ca5d61c5e977c3304309330d6662b4efc51d6ce",
|
||||
"https://deno.land/x/esbuild@v0.14.51/mod.d.ts": "c142324d0383c39de0d7660cd207a7f7f52c7198a13d7d3281c0d636a070f441",
|
||||
"https://deno.land/x/esbuild@v0.14.51/mod.js": "7432566c71fac77637822dc230319c7392a2d2fef51204c9d12c956d7093c279",
|
||||
"https://deno.land/x/esbuild@v0.14.51/wasm.d.ts": "c142324d0383c39de0d7660cd207a7f7f52c7198a13d7d3281c0d636a070f441",
|
||||
@ -284,6 +346,9 @@
|
||||
"https://deno.land/x/fresh@1.1.2/src/server/types.ts": "dde992ab4ee635df71a7fc96fe4cd85943c1a9286ea8eb586563d5f5ca154955",
|
||||
"https://deno.land/x/importmap@0.2.1/_util.ts": "ada9a9618b537e6c0316c048a898352396c882b9f2de38aba18fd3f2950ede89",
|
||||
"https://deno.land/x/importmap@0.2.1/mod.ts": "ae3d1cd7eabd18c01a4960d57db471126b020f23b37ef14e1359bbb949227ade",
|
||||
"https://deno.land/x/progress@v1.3.0/deps.ts": "83050e627263931d853ba28b7c15c80bf4be912bea7e0d3d13da2bc0aaf7889d",
|
||||
"https://deno.land/x/progress@v1.3.0/mod.ts": "de6a75f14964a870facb51b902d39d7fa391e2b4281af062c5c4525af0fa6796",
|
||||
"https://deno.land/x/progress@v1.3.0/multi.ts": "8cd7c2df6b00148fa0cd60554693b337d85e95a823f40b7c1ec2ba0d301263db",
|
||||
"https://deno.land/x/progress@v1.3.4/deps.ts": "83050e627263931d853ba28b7c15c80bf4be912bea7e0d3d13da2bc0aaf7889d",
|
||||
"https://deno.land/x/progress@v1.3.4/mod.ts": "ca65cf56c63d48ac4806f62a6ee5e5889dc19b8bd9a3be2bfeee6c8c4a483786",
|
||||
"https://deno.land/x/progress@v1.3.4/multi.ts": "755f05ce3d1f859142c6a1e67972f8765ee29eac7bfdec8126008c312addbeef",
|
||||
|
1
dev.ts
1
dev.ts
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env -S deno run -A --watch=static/,routes/
|
||||
|
||||
import dev from "$fresh/dev.ts";
|
||||
//import "https://deno.land/std@0.162.0/dotenv/load.ts";
|
||||
|
||||
await dev(import.meta.url, "./main.ts");
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
from_url: https://github.com/donnemartin/awesome-aws
|
||||
name: aws-cdk
|
||||
author: 0x0d
|
||||
author: "0x0d"
|
||||
star: 9537
|
||||
fork: 2905
|
||||
desc: "The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code"
|
||||
|
14
fresh.gen.ts
14
fresh.gen.ts
@ -5,19 +5,25 @@
|
||||
import config from "./deno.json" assert { type: "json" };
|
||||
import * as $0 from "./routes/api/_list.ts";
|
||||
import * as $1 from "./routes/api/_query.ts";
|
||||
import * as $2 from "./routes/index.tsx";
|
||||
import * as $2 from "./routes/dynamic.tsx";
|
||||
import * as $3 from "./routes/index.tsx";
|
||||
import * as $$0 from "./islands/Counter.tsx";
|
||||
import * as $$1 from "./islands/RepoViewer.tsx";
|
||||
import * as $$1 from "./islands/MySearchBar.tsx";
|
||||
import * as $$2 from "./islands/RepoViewer.tsx";
|
||||
import * as $$3 from "./islands/Search.tsx";
|
||||
|
||||
const manifest = {
|
||||
routes: {
|
||||
"./routes/api/_list.ts": $0,
|
||||
"./routes/api/_query.ts": $1,
|
||||
"./routes/index.tsx": $2,
|
||||
"./routes/dynamic.tsx": $2,
|
||||
"./routes/index.tsx": $3,
|
||||
},
|
||||
islands: {
|
||||
"./islands/Counter.tsx": $$0,
|
||||
"./islands/RepoViewer.tsx": $$1,
|
||||
"./islands/MySearchBar.tsx": $$1,
|
||||
"./islands/RepoViewer.tsx": $$2,
|
||||
"./islands/Search.tsx": $$3,
|
||||
},
|
||||
baseUrl: import.meta.url,
|
||||
config,
|
||||
|
@ -10,6 +10,6 @@
|
||||
"twind/": "https://esm.sh/twind@0.16.17/",
|
||||
"cliffy": "https://deno.land/x/cliffy@v0.25.4/mod.ts",
|
||||
|
||||
"std/": "https://deno.land/std/"
|
||||
"std/": "https://deno.land/std@0.166.0/"
|
||||
}
|
||||
}
|
||||
|
12
islands/MySearchBar.tsx
Normal file
12
islands/MySearchBar.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { useState } from "preact/hooks";
|
||||
import { SearchBar } from "../components/SearchBar.tsx";
|
||||
|
||||
|
||||
export default function MySearch({query}: {query?: string}) {
|
||||
const [searchValue, setSearchValue] = useState(query ?? "");
|
||||
|
||||
return (
|
||||
<SearchBar value={searchValue} onChange={(v) => {setSearchValue(v)}} onSubmit={()=>{
|
||||
window.location.href = `/?q=${searchValue}`;
|
||||
}} />);
|
||||
}
|
@ -18,7 +18,7 @@ function RepoItem(props: RepoData) {
|
||||
const opacity = useRelativeTopOppacity({elem: ref});
|
||||
const { name, description, url, author, stars, tags, forks } = props;
|
||||
return (
|
||||
<div ref={ref} class="flex flex-col bg-white rounded transition-opacity"
|
||||
<div ref={ref} class="flex flex-col bg-white rounded"
|
||||
style={`opacity: ${opacity}`}>
|
||||
<div class="flex flex-col flex-grow p-4">
|
||||
<a class="text-xl font-bold text-gray-900 hover:text-gray-700 flex-auto"
|
||||
|
67
islands/Search.tsx
Normal file
67
islands/Search.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { RepoData } from "../api/repo.ts";
|
||||
import { SearchBar } from "../components/SearchBar.tsx";
|
||||
import RepoViewer from "./RepoViewer.tsx";
|
||||
|
||||
|
||||
export default function Search(props:{query?: string}) {
|
||||
const [searchValue, setSearchValue] = useState(props.query ?? "");
|
||||
const [searchResults, setSearchResults] = useState<RepoData[] | null>(null);
|
||||
useEffect(() => {
|
||||
// on mount
|
||||
search(searchValue);
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
const callback = (ev: PopStateEvent)=>{
|
||||
// pop state
|
||||
if(ev.state && ev.state.q){
|
||||
const q = ev.state.q;
|
||||
setSearchValue(q);
|
||||
search(q);
|
||||
}
|
||||
else{
|
||||
setSearchValue("");
|
||||
search("");
|
||||
}
|
||||
}
|
||||
addEventListener("popstate", callback);
|
||||
return ()=>{
|
||||
removeEventListener("popstate", callback);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchValue) {
|
||||
document.title = `Search: ${searchValue}`;
|
||||
} else {
|
||||
document.title = "Search";
|
||||
}
|
||||
},[searchValue]);
|
||||
|
||||
return (<>
|
||||
<SearchBar value={searchValue} onChange={(v) => {setSearchValue(v)}} onSubmit={()=>{
|
||||
//window.location.href = `/?q=${searchValue}`;
|
||||
history.pushState({q:searchValue}, "", `/?q=${searchValue}`);
|
||||
search(searchValue);
|
||||
}} />
|
||||
<RepoViewer repos={searchResults ?? []} />
|
||||
</>);
|
||||
function search(searchValue: string) {
|
||||
if (searchValue) {
|
||||
console.log("searching", searchValue);
|
||||
fetch(`/api/_query?q=${searchValue}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setSearchResults(data);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fetch(`/api/_list`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
setSearchResults(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
import { HandlerContext } from "$fresh/server.ts";
|
||||
import { SAMPLE_DATA, RepoData } from "../../api/repo.ts";
|
||||
import { HandlerContext, Handlers } from "$fresh/server.ts";
|
||||
import { SAMPLE_DATA, RepoData, searchRepos, getRepos } from "../../api/repo.ts";
|
||||
|
||||
export const handler = (_req: Request, _ctx: HandlerContext): Response => {
|
||||
return new Response(JSON.stringify(SAMPLE_DATA), {
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
});
|
||||
export const handler: Handlers = {
|
||||
async GET(_req, _ctx) {
|
||||
const repos = await getRepos();
|
||||
return new Response(JSON.stringify(repos), {
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
@ -1,10 +1,18 @@
|
||||
import { HandlerContext } from "$fresh/server.ts";
|
||||
import { SAMPLE_DATA, RepoData } from "../../api/repo.ts";
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { searchRepos, getRepos } from "../../api/repo.ts";
|
||||
|
||||
export const handler = (_req: Request, _ctx: HandlerContext): Response => {
|
||||
return new Response(JSON.stringify(SAMPLE_DATA), {
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
});
|
||||
};
|
||||
export const handler: Handlers = {
|
||||
async GET(req, _ctx) {
|
||||
const url = new URL(req.url);
|
||||
const q = url.searchParams.get("q");
|
||||
const repos = q != null ? await searchRepos(q, {
|
||||
limit: 10,
|
||||
offset: 0
|
||||
}) : await getRepos();
|
||||
return new Response(JSON.stringify(repos), {
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
30
routes/dynamic.tsx
Normal file
30
routes/dynamic.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { Head } from "$fresh/runtime.ts";
|
||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||
import { RepoData, getRepos, searchRepos } from "../api/repo.ts";
|
||||
import { useState } from "preact/hooks";
|
||||
import { SearchBar } from "../components/SearchBar.tsx";
|
||||
import RepoViewer from "../islands/RepoViewer.tsx";
|
||||
import Search from "../islands/Search.tsx";
|
||||
|
||||
export const handler: Handlers = {
|
||||
GET: (req, ctx) => {
|
||||
const url = new URL(req.url);
|
||||
const searchParams = url.searchParams;
|
||||
const query = searchParams.get("q");
|
||||
return ctx.render({query})
|
||||
},
|
||||
};
|
||||
|
||||
export default function Home({ data }: PageProps<{query?: string}>) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Search Github Awesome App</title>
|
||||
</Head>
|
||||
<div class="p-4 mx-auto max-w-screen-md">
|
||||
<h1 class="text-4xl font-bold">Search Github Awesome App</h1>
|
||||
<Search query={data.query}></Search>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -4,29 +4,27 @@ import { RepoData, getRepos, searchRepos } from "../api/repo.ts";
|
||||
import { useState } from "preact/hooks";
|
||||
import { SearchBar } from "../components/SearchBar.tsx";
|
||||
import RepoViewer from "../islands/RepoViewer.tsx";
|
||||
import Search from "../islands/Search.tsx";
|
||||
import MySearch from "../islands/MySearchBar.tsx";
|
||||
|
||||
export const handler: Handlers<RepoData[] | null> = {
|
||||
async GET(req, ctx) {
|
||||
try {
|
||||
const url = new URL(req.url);
|
||||
const query = url.searchParams.get("q");
|
||||
if (query) {
|
||||
const repos = await searchRepos(query);
|
||||
return ctx.render(repos);
|
||||
}
|
||||
else {
|
||||
const repos = await getRepos();
|
||||
return ctx.render(repos);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return ctx.render(null);
|
||||
export const handler: Handlers = {
|
||||
GET: async(req, ctx) => {
|
||||
const url = new URL(req.url);
|
||||
const searchParams = url.searchParams;
|
||||
const query = searchParams.get("q");
|
||||
if(query){
|
||||
const data = await searchRepos(query);
|
||||
return ctx.render({repos:data, query})
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
const data = await getRepos();
|
||||
return ctx.render({repos:data, query: ""})
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default function Home({ data }: PageProps<{repos: RepoData[], query: string}>) {
|
||||
|
||||
export default function Home({ data }: PageProps<RepoData[] | null>) {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@ -34,8 +32,8 @@ export default function Home({ data }: PageProps<RepoData[] | null>) {
|
||||
</Head>
|
||||
<div class="p-4 mx-auto max-w-screen-md">
|
||||
<h1 class="text-4xl font-bold">Search Github Awesome App</h1>
|
||||
<SearchBar value={searchValue} onChange={() => { }} />
|
||||
<RepoViewer repos={data || []} />
|
||||
<MySearch query={data.query}></MySearch>
|
||||
<RepoViewer repos={data.repos ?? []} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -60,7 +60,7 @@ export function useRelativeTopOppacity({elem}:{elem: RefObject<Element>}) {
|
||||
|
||||
if (intersect >= 0) {
|
||||
let v = Math.min(Math.max(intersect, 0), 1);
|
||||
v *= 4/3;
|
||||
//v *= 4/3;
|
||||
v = Math.min(Math.max(v, 0), 1);
|
||||
setOpacity(v);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user