diff --git a/.github/workflows/self_deploy.yml b/.github/workflows/self_deploy.yml index 0e36b70..9790571 100644 --- a/.github/workflows/self_deploy.yml +++ b/.github/workflows/self_deploy.yml @@ -8,10 +8,10 @@ on: jobs: deploy: runs-on: ubuntu-latest - + env: DEPLOY_URL: ${{ secrets.DEPLOY_URL }} - + steps: - name: Send deployment request id: deploy-request @@ -24,11 +24,11 @@ jobs: --silent \ --show-error \ "${{ secrets.DEPLOY_URL }}" || echo "Failed to send request") - + if [ $? -eq 0 ]; then echo "Deployment request sent successfully" echo "Response: $RESPONSE" else echo "Error sending deployment request" exit 1 - fi \ No newline at end of file + fi diff --git a/.gitignore b/.gitignore index a6a5428..166b664 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ logs # Scraping data .venv -__pycache__ \ No newline at end of file +__pycache__ diff --git a/components/ui/command/Command.vue b/components/ui/command/Command.vue index 9597010..fa56cf7 100644 --- a/components/ui/command/Command.vue +++ b/components/ui/command/Command.vue @@ -1,30 +1,33 @@ diff --git a/components/ui/command/CommandDialog.vue b/components/ui/command/CommandDialog.vue index b7b0a91..07aafdf 100644 --- a/components/ui/command/CommandDialog.vue +++ b/components/ui/command/CommandDialog.vue @@ -1,19 +1,21 @@ - + diff --git a/components/ui/command/CommandEmpty.vue b/components/ui/command/CommandEmpty.vue index 58795f2..7a615b2 100644 --- a/components/ui/command/CommandEmpty.vue +++ b/components/ui/command/CommandEmpty.vue @@ -1,25 +1,32 @@ - + diff --git a/components/ui/command/CommandGroup.vue b/components/ui/command/CommandGroup.vue index fd4f5b3..a2b7463 100644 --- a/components/ui/command/CommandGroup.vue +++ b/components/ui/command/CommandGroup.vue @@ -1,44 +1,55 @@ - + {{ heading }} diff --git a/components/ui/command/CommandInput.vue b/components/ui/command/CommandInput.vue index 4c3b963..abf55fe 100644 --- a/components/ui/command/CommandInput.vue +++ b/components/ui/command/CommandInput.vue @@ -1,27 +1,33 @@ @@ -31,7 +37,12 @@ const { filterState } = useCommand() v-bind="{ ...forwardedProps, ...$attrs }" v-model="filterState.search" auto-focus - :class="cn('flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)" + :class=" + cn( + 'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', + props.class, + ) + " /> diff --git a/components/ui/command/CommandItem.vue b/components/ui/command/CommandItem.vue index 97365e1..ccc64e5 100644 --- a/components/ui/command/CommandItem.vue +++ b/components/ui/command/CommandItem.vue @@ -1,32 +1,39 @@ @@ -68,10 +76,17 @@ onUnmounted(() => { v-bind="forwarded" :id="id" ref="itemRef" - :class="cn('relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0', props.class)" - @select="() => { - filterState.search = '' - }" + :class=" + cn( + 'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0', + props.class, + ) + " + @select=" + () => { + filterState.search = ''; + } + " > diff --git a/components/ui/command/CommandList.vue b/components/ui/command/CommandList.vue index 83c7d95..ea95a69 100644 --- a/components/ui/command/CommandList.vue +++ b/components/ui/command/CommandList.vue @@ -1,22 +1,27 @@ - + diff --git a/components/ui/command/CommandSeparator.vue b/components/ui/command/CommandSeparator.vue index 4122020..ac9afab 100644 --- a/components/ui/command/CommandSeparator.vue +++ b/components/ui/command/CommandSeparator.vue @@ -1,16 +1,18 @@ diff --git a/components/ui/command/CommandShortcut.vue b/components/ui/command/CommandShortcut.vue index 0d4da92..cece81d 100644 --- a/components/ui/command/CommandShortcut.vue +++ b/components/ui/command/CommandShortcut.vue @@ -1,14 +1,18 @@ - + diff --git a/components/ui/command/index.ts b/components/ui/command/index.ts index cb48e1e..6892a4e 100644 --- a/components/ui/command/index.ts +++ b/components/ui/command/index.ts @@ -1,25 +1,29 @@ -import type { Ref } from 'vue' -import { createContext } from 'reka-ui' +import type { Ref } from "vue"; +import { createContext } from "reka-ui"; -export { default as Command } from './Command.vue' -export { default as CommandDialog } from './CommandDialog.vue' -export { default as CommandEmpty } from './CommandEmpty.vue' -export { default as CommandGroup } from './CommandGroup.vue' -export { default as CommandInput } from './CommandInput.vue' -export { default as CommandItem } from './CommandItem.vue' -export { default as CommandList } from './CommandList.vue' -export { default as CommandSeparator } from './CommandSeparator.vue' -export { default as CommandShortcut } from './CommandShortcut.vue' +export { default as Command } from "./Command.vue"; +export { default as CommandDialog } from "./CommandDialog.vue"; +export { default as CommandEmpty } from "./CommandEmpty.vue"; +export { default as CommandGroup } from "./CommandGroup.vue"; +export { default as CommandInput } from "./CommandInput.vue"; +export { default as CommandItem } from "./CommandItem.vue"; +export { default as CommandList } from "./CommandList.vue"; +export { default as CommandSeparator } from "./CommandSeparator.vue"; +export { default as CommandShortcut } from "./CommandShortcut.vue"; export const [useCommand, provideCommandContext] = createContext<{ - allItems: Ref> - allGroups: Ref>> + allItems: Ref>; + allGroups: Ref>>; filterState: { - search: string - filtered: { count: number, items: Map, groups: Set } - } -}>('Command') + search: string; + filtered: { + count: number; + items: Map; + groups: Set; + }; + }; +}>("Command"); export const [useCommandGroup, provideCommandGroupContext] = createContext<{ - id?: string -}>('CommandGroup') + id?: string; +}>("CommandGroup"); diff --git a/components/ui/dialog/Dialog.vue b/components/ui/dialog/Dialog.vue index 9fc9c7d..ded4ed5 100644 --- a/components/ui/dialog/Dialog.vue +++ b/components/ui/dialog/Dialog.vue @@ -1,10 +1,15 @@ diff --git a/components/ui/dialog/DialogClose.vue b/components/ui/dialog/DialogClose.vue index ba036b5..d05a7a9 100644 --- a/components/ui/dialog/DialogClose.vue +++ b/components/ui/dialog/DialogClose.vue @@ -1,7 +1,7 @@ diff --git a/components/ui/dialog/DialogContent.vue b/components/ui/dialog/DialogContent.vue index d84f271..94403fa 100644 --- a/components/ui/dialog/DialogContent.vue +++ b/components/ui/dialog/DialogContent.vue @@ -1,6 +1,6 @@ @@ -35,7 +37,8 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits) cn( 'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg', props.class, - )" + ) + " > diff --git a/components/ui/dialog/DialogDescription.vue b/components/ui/dialog/DialogDescription.vue index 5afbab0..3ca2328 100644 --- a/components/ui/dialog/DialogDescription.vue +++ b/components/ui/dialog/DialogDescription.vue @@ -1,17 +1,23 @@ diff --git a/components/ui/dialog/DialogFooter.vue b/components/ui/dialog/DialogFooter.vue index ac2d0c1..771e405 100644 --- a/components/ui/dialog/DialogFooter.vue +++ b/components/ui/dialog/DialogFooter.vue @@ -1,8 +1,8 @@ diff --git a/components/ui/dialog/DialogHeader.vue b/components/ui/dialog/DialogHeader.vue index b2c9085..a76aba9 100644 --- a/components/ui/dialog/DialogHeader.vue +++ b/components/ui/dialog/DialogHeader.vue @@ -1,10 +1,10 @@ diff --git a/components/ui/dialog/DialogScrollContent.vue b/components/ui/dialog/DialogScrollContent.vue index ee6b5e2..f651ea2 100644 --- a/components/ui/dialog/DialogScrollContent.vue +++ b/components/ui/dialog/DialogScrollContent.vue @@ -1,6 +1,6 @@ @@ -37,13 +39,18 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits) ) " v-bind="forwarded" - @pointer-down-outside="(event) => { - const originalEvent = event.detail.originalEvent; - const target = originalEvent.target as HTMLElement; - if (originalEvent.offsetX > target.clientWidth || originalEvent.offsetY > target.clientHeight) { - event.preventDefault(); + @pointer-down-outside=" + (event) => { + const originalEvent = event.detail.originalEvent; + const target = originalEvent.target as HTMLElement; + if ( + originalEvent.offsetX > target.clientWidth || + originalEvent.offsetY > target.clientHeight + ) { + event.preventDefault(); + } } - }" + " > diff --git a/components/ui/dialog/DialogTitle.vue b/components/ui/dialog/DialogTitle.vue index 30cbb36..3520326 100644 --- a/components/ui/dialog/DialogTitle.vue +++ b/components/ui/dialog/DialogTitle.vue @@ -1,27 +1,26 @@ diff --git a/components/ui/dialog/DialogTrigger.vue b/components/ui/dialog/DialogTrigger.vue index 2984f37..9bb3900 100644 --- a/components/ui/dialog/DialogTrigger.vue +++ b/components/ui/dialog/DialogTrigger.vue @@ -1,7 +1,7 @@ diff --git a/components/ui/dialog/index.ts b/components/ui/dialog/index.ts index ca8cfea..e2b3a15 100644 --- a/components/ui/dialog/index.ts +++ b/components/ui/dialog/index.ts @@ -1,9 +1,9 @@ -export { default as Dialog } from './Dialog.vue' -export { default as DialogClose } from './DialogClose.vue' -export { default as DialogContent } from './DialogContent.vue' -export { default as DialogDescription } from './DialogDescription.vue' -export { default as DialogFooter } from './DialogFooter.vue' -export { default as DialogHeader } from './DialogHeader.vue' -export { default as DialogScrollContent } from './DialogScrollContent.vue' -export { default as DialogTitle } from './DialogTitle.vue' -export { default as DialogTrigger } from './DialogTrigger.vue' +export { default as Dialog } from "./Dialog.vue"; +export { default as DialogClose } from "./DialogClose.vue"; +export { default as DialogContent } from "./DialogContent.vue"; +export { default as DialogDescription } from "./DialogDescription.vue"; +export { default as DialogFooter } from "./DialogFooter.vue"; +export { default as DialogHeader } from "./DialogHeader.vue"; +export { default as DialogScrollContent } from "./DialogScrollContent.vue"; +export { default as DialogTitle } from "./DialogTitle.vue"; +export { default as DialogTrigger } from "./DialogTrigger.vue"; diff --git a/components/ui/hover-card/HoverCard.vue b/components/ui/hover-card/HoverCard.vue index ea87bf2..3741c97 100644 --- a/components/ui/hover-card/HoverCard.vue +++ b/components/ui/hover-card/HoverCard.vue @@ -1,10 +1,15 @@ diff --git a/components/ui/hover-card/HoverCardContent.vue b/components/ui/hover-card/HoverCardContent.vue index 687b339..792cdfc 100644 --- a/components/ui/hover-card/HoverCardContent.vue +++ b/components/ui/hover-card/HoverCardContent.vue @@ -1,27 +1,27 @@ diff --git a/components/ui/hover-card/HoverCardTrigger.vue b/components/ui/hover-card/HoverCardTrigger.vue index bed301a..3f3b187 100644 --- a/components/ui/hover-card/HoverCardTrigger.vue +++ b/components/ui/hover-card/HoverCardTrigger.vue @@ -1,7 +1,7 @@ diff --git a/components/ui/hover-card/index.ts b/components/ui/hover-card/index.ts index 9e4ccc2..b16bbcf 100644 --- a/components/ui/hover-card/index.ts +++ b/components/ui/hover-card/index.ts @@ -1,3 +1,3 @@ -export { default as HoverCard } from './HoverCard.vue' -export { default as HoverCardContent } from './HoverCardContent.vue' -export { default as HoverCardTrigger } from './HoverCardTrigger.vue' +export { default as HoverCard } from "./HoverCard.vue"; +export { default as HoverCardContent } from "./HoverCardContent.vue"; +export { default as HoverCardTrigger } from "./HoverCardTrigger.vue"; diff --git a/createDatabase.ts b/createDatabase.ts index 03e9acc..32e4ea3 100644 --- a/createDatabase.ts +++ b/createDatabase.ts @@ -42,7 +42,6 @@ create table if not exists newsProvidersZh ( ) `; - const createGoLinks = await sql` create table if not exists go_links { uuid text primary key, @@ -51,7 +50,7 @@ create table if not exists go_links { forwardUrl text not null, created_at timestampz default current_timestamp } -` +`; /* const createAdminPosts = await sql` create table if not exists adminPosts ( diff --git a/docker-compose.yml b/docker-compose.yml index 9d6f668..0063c38 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: ports: - "127.0.0.1:36694:80" volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro + - /var/run/docker.sock:/var/run/docker.sock:ro networks: - app-network newsanalyze-service: @@ -18,7 +18,7 @@ services: retries: 3 networks: - app-network - labels: + labels: - "traefik.enable=true" - "traefik.http.routers.newsanalyze.rule=Host(`news.yuanhau.com`)" - "traefik.http.routers.newsanalyze.entrypoints=webinternal" diff --git a/nuxt.config.ts b/nuxt.config.ts index 0950242..4351864 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -111,6 +111,6 @@ export default defineNuxtConfig({ componentDir: "./components/ui", }, nitro: { - preset: "bun" // This is dumb. - } + preset: "bun", // This is dumb. + }, }); diff --git a/pages/newsOrgs.vue b/pages/newsOrgs.vue index f48d05e..d7cf98a 100644 --- a/pages/newsOrgs.vue +++ b/pages/newsOrgs.vue @@ -2,7 +2,7 @@ const { t } = useI18n(); - - {{ t("newsOrgs.title") }} - + + {{ t("newsOrgs.title") }} + diff --git a/pages/topic/view/[slug].vue b/pages/topic/view/[slug].vue index f3846a7..b5cdce0 100644 --- a/pages/topic/view/[slug].vue +++ b/pages/topic/view/[slug].vue @@ -1,9 +1,16 @@ - - - - - \ No newline at end of file + + + + diff --git a/scraping/README.md b/scraping/README.md new file mode 100644 index 0000000..208f34d --- /dev/null +++ b/scraping/README.md @@ -0,0 +1,3 @@ +# Scraping + +This file contains the code for scraping the news from websites. And storing the data into the a postgres database. diff --git a/scraping/main.py b/scraping/main.py index d6f9e00..ac3f97e 100644 --- a/scraping/main.py +++ b/scraping/main.py @@ -1,12 +1,7 @@ -import scrapy +from urllib.request import urlopen -class BlogSpider(scrapy.Spider): - name = 'blogspider' - start_urls = ['https://news.google.com/u/4/home?hl=zh-TW&gl=TW&ceid=TW:zh-Hant&pageId=none'] +url = "https://tw.news.yahoo.com/" - def parse(self, response): - for title in response.css('.oxy-post-title'): - yield {'title': title.css('::text').get()} +page = urlopen(url) - for next_page in response.css('a.next'): - yield response.follow(next_page, self.parse) \ No newline at end of file +page \ No newline at end of file diff --git a/scraping/requirements.txt b/scraping/requirements.txt index ccee6de..6530294 100644 --- a/scraping/requirements.txt +++ b/scraping/requirements.txt @@ -1 +1 @@ -scrapy \ No newline at end of file +urlopen \ No newline at end of file diff --git a/server/api/ai/chat/[slug].ts b/server/api/ai/chat/[slug].ts index 90f3ea0..9075f00 100644 --- a/server/api/ai/chat/[slug].ts +++ b/server/api/ai/chat/[slug].ts @@ -1,35 +1,35 @@ -import { Groq } from 'groq-sdk'; +import { Groq } from "groq-sdk"; import sql from "~/server/components/postgres"; const groq = new Groq(); export default defineEventHandler(async (event) => { - const slug = getRouterParam(event, 'slug'); + const slug = getRouterParam(event, "slug"); const body = await readBody(event); const fetchNewsArticle = await sql` select * from newArticle where slug = ${slug} `; const chatCompletion = await groq.chat.completions.create({ - "messages": [ - { - "role": "user", - "content": `${body}` - }, - { - "role": "system", - "content": `You are a news chat, the following content will be used to chat with the user title: ${fetchNewsArticle.title}\n content: ${fetchNewsArticle.content}` - } - ], - "model": "llama3-70b-8192", - "temperature": 1, - "max_completion_tokens": 1024, - "top_p": 1, - "stream": true, - "stop": null - }); - + messages: [ + { + role: "user", + content: `${body}`, + }, + { + role: "system", + content: `You are a news chat, the following content will be used to chat with the user title: ${fetchNewsArticle.title}\n content: ${fetchNewsArticle.content}`, + }, + ], + model: "llama3-70b-8192", + temperature: 1, + max_completion_tokens: 1024, + top_p: 1, + stream: true, + stop: null, + }); + for await (const chunk of chatCompletion) { - process.stdout.write(chunk.choices[0]?.delta?.content || ''); + process.stdout.write(chunk.choices[0]?.delta?.content || ""); } -}) \ No newline at end of file +}); diff --git a/server/api/ai/summerize/[slug].ts b/server/api/ai/summerize/[slug].ts index d3148d0..2c3e025 100644 --- a/server/api/ai/summerize/[slug].ts +++ b/server/api/ai/summerize/[slug].ts @@ -1,34 +1,34 @@ -import { Groq } from 'groq-sdk'; +import { Groq } from "groq-sdk"; import sql from "~/server/components/postgres"; const groq = new Groq(); export default defineEventHandler(async (event) => { - const slug = getRouterParam(event, 'slug'); + const slug = getRouterParam(event, "slug"); const fetchNewsArticle = await sql` select * from newArticle where slug = ${slug} `; const chatCompletion = await groq.chat.completions.create({ - "messages": [ - { - "role": "user", - "content": `${fetchNewsArticle.title}\n${fetchNewsArticle.content}` - }, - { - "role": "system", - "content": `You are a news summarizer. You will be given a news article and you will summarize it into a short paragraph.` - } - ], - "model": "llama3-70b-8192", - "temperature": 1, - "max_completion_tokens": 1024, - "top_p": 1, - "stream": true, - "stop": null - }); - + messages: [ + { + role: "user", + content: `${fetchNewsArticle.title}\n${fetchNewsArticle.content}`, + }, + { + role: "system", + content: `You are a news summarizer. You will be given a news article and you will summarize it into a short paragraph.`, + }, + ], + model: "llama3-70b-8192", + temperature: 1, + max_completion_tokens: 1024, + top_p: 1, + stream: true, + stop: null, + }); + for await (const chunk of chatCompletion) { - process.stdout.write(chunk.choices[0]?.delta?.content || ''); + process.stdout.write(chunk.choices[0]?.delta?.content || ""); } -}) \ No newline at end of file +}); diff --git a/server/api/fetcharticle/[slug].ts b/server/api/fetcharticle/[slug].ts index 6195044..1bfe5b9 100644 --- a/server/api/fetcharticle/[slug].ts +++ b/server/api/fetcharticle/[slug].ts @@ -1,28 +1,28 @@ import sql from "~/server/components/postgres"; export default defineEventHandler(async (event) => { - const slug = getRouterParam(event, 'slug'); - - // Validate and sanitize the slug - if (!slug || typeof slug !== 'string') { - throw createError({ - statusCode: 400, - message: 'Invalid slug parameter' - }); - } - const cleanSlug = slug.replace(/[^a-zA-Z0-9-_]/g, ''); + const slug = getRouterParam(event, "slug"); - try { - const result = await sql` + // Validate and sanitize the slug + if (!slug || typeof slug !== "string") { + throw createError({ + statusCode: 400, + message: "Invalid slug parameter", + }); + } + const cleanSlug = slug.replace(/[^a-zA-Z0-9-_]/g, ""); + + try { + const result = await sql` select * from articles where slug = ${cleanSlug} - ` - - return result.rows[0] || null; - } catch (error) { - console.error('Database error:', error); - throw createError({ - statusCode: 500, - message: 'Internal server error' - }); - } -}); \ No newline at end of file + `; + + return result.rows[0] || null; + } catch (error) { + console.error("Database error:", error); + throw createError({ + statusCode: 500, + message: "Internal server error", + }); + } +}); diff --git a/server/components/postgres.ts b/server/components/postgres.ts index de922b2..1c8394f 100644 --- a/server/components/postgres.ts +++ b/server/components/postgres.ts @@ -1,7 +1,7 @@ import { SQL } from "bun"; const postgres = new SQL({ - url: process.env.POSTGRES_URL, -}) + url: process.env.POSTGRES_URL, +}); -export default postgres; \ No newline at end of file +export default postgres; diff --git a/server/routes/go/[slug].ts b/server/routes/go/[slug].ts index 7526b28..a6b1c79 100644 --- a/server/routes/go/[slug].ts +++ b/server/routes/go/[slug].ts @@ -1,24 +1,24 @@ import sql from "~/server/components/postgres"; export default defineEventHandler(async (event) => { - const slug = getRouterParam(event, 'slug'); - if (!slug || typeof slug !== 'string') { - throw createError({ - statusCode: 400, - message: 'Invalid slug parameter' - }); - } - const cleanSlug = slug.replace(/[^a-zA-Z0-9-_]/g, ''); - try { - const result = await sql` + const slug = getRouterParam(event, "slug"); + if (!slug || typeof slug !== "string") { + throw createError({ + statusCode: 400, + message: "Invalid slug parameter", + }); + } + const cleanSlug = slug.replace(/[^a-zA-Z0-9-_]/g, ""); + try { + const result = await sql` select * from go_links where slug = ${cleanSlug} - ` - return result.rows[0] || null; - } catch (error) { - console.error('Database error:', error); - throw createError({ - statusCode: 500, - message: 'Internal server error' - }); - } -}) \ No newline at end of file + `; + return result.rows[0] || null; + } catch (error) { + console.error("Database error:", error); + throw createError({ + statusCode: 500, + message: "Internal server error", + }); + } +});