aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.dockerignore6
-rw-r--r--.gitignore4
-rw-r--r--Dockerfile5
-rw-r--r--backend/graphql/generated.go214
-rw-r--r--backend/main.go30
-rw-r--r--backend/public/dummy.txt4
-rw-r--r--common/graphql/schema.graphql232
-rw-r--r--compose.yaml2
-rw-r--r--frontend/graphql-codegen.ts4
-rw-r--r--frontend/public/vite.svg1
l---------frontend/src/graphql/schema.graphql1
11 files changed, 247 insertions, 256 deletions
diff --git a/.dockerignore b/.dockerignore
index 48e8cc4..10446ea 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,6 +1,4 @@
/backend/feedaka
-/frontend/node_modules
+/data/
/frontend/dist
-
-/feedaka.db
-/feedaka.db-journal
+/frontend/node_modules
diff --git a/.gitignore b/.gitignore
index 9728745..5345969 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,2 @@
/backend/feedaka
-
-/feedaka.db
-/feedaka.db-journal
+/data/
diff --git a/Dockerfile b/Dockerfile
index f60987e..c73319b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,11 +6,12 @@ COPY frontend/package.json frontend/package-lock.json ./
RUN npm install
COPY frontend/ ./
+COPY common/graphql/schema.graphql src/graphql/schema.graphql
RUN npm run build
##########################################
-FROM golang:1.23-alpine AS backend-builder
+FROM golang:1.24-alpine AS backend-builder
WORKDIR /app
@@ -19,7 +20,7 @@ COPY backend/go.mod backend/go.sum ./
RUN go mod download
COPY backend/ ./
-COPY --from=frontend-builder /app/dist/style.css ./static/style.css
+COPY --from=frontend-builder /app/dist/ ./public/
RUN CGO_ENABLED=1 GOOS=linux go build -o feedaka main.go
##########################################
diff --git a/backend/graphql/generated.go b/backend/graphql/generated.go
index b09d4d8..e4790bd 100644
--- a/backend/graphql/generated.go
+++ b/backend/graphql/generated.go
@@ -436,140 +436,140 @@ var sources = []*ast.Source{
Represents a feed subscription in the system
"""
type Feed {
- """
- Unique identifier for the feed
- """
- id: ID!
+ """
+ Unique identifier for the feed
+ """
+ id: ID!
- """
- URL of the RSS/Atom feed
- """
- url: String!
+ """
+ URL of the RSS/Atom feed
+ """
+ url: String!
- """
- Title of the feed (extracted from feed metadata)
- """
- title: String!
+ """
+ Title of the feed (extracted from feed metadata)
+ """
+ title: String!
- """
- Timestamp when the feed was last fetched
- """
- fetchedAt: DateTime!
+ """
+ Timestamp when the feed was last fetched
+ """
+ fetchedAt: DateTime!
- """
- Whether the user is currently subscribed to this feed
- """
- isSubscribed: Boolean!
+ """
+ Whether the user is currently subscribed to this feed
+ """
+ isSubscribed: Boolean!
- """
- Articles belonging to this feed
- """
- articles: [Article!]!
+ """
+ Articles belonging to this feed
+ """
+ articles: [Article!]!
}
"""
Represents an individual article/post from a feed
"""
type Article {
- """
- Unique identifier for the article
- """
- id: ID!
-
- """
- ID of the feed this article belongs to
- """
- feedId: ID!
-
- """
- GUID from the RSS/Atom feed (unique identifier from feed)
- """
- guid: String!
-
- """
- Title of the article
- """
- title: String!
-
- """
- URL/link to the original article
- """
- url: String!
-
- """
- Whether the article has been marked as read
- """
- isRead: Boolean!
-
- """
- The feed this article belongs to
- """
- feed: Feed!
+ """
+ Unique identifier for the article
+ """
+ id: ID!
+
+ """
+ ID of the feed this article belongs to
+ """
+ feedId: ID!
+
+ """
+ GUID from the RSS/Atom feed (unique identifier from feed)
+ """
+ guid: String!
+
+ """
+ Title of the article
+ """
+ title: String!
+
+ """
+ URL/link to the original article
+ """
+ url: String!
+
+ """
+ Whether the article has been marked as read
+ """
+ isRead: Boolean!
+
+ """
+ The feed this article belongs to
+ """
+ feed: Feed!
}
"""
Root query type for reading data
"""
type Query {
- """
- Get all feeds with their metadata
- """
- feeds: [Feed!]!
+ """
+ Get all feeds with their metadata
+ """
+ feeds: [Feed!]!
- """
- Get all unread articles across all feeds
- """
- unreadArticles: [Article!]!
+ """
+ Get all unread articles across all feeds
+ """
+ unreadArticles: [Article!]!
- """
- Get all read articles across all feeds
- """
- readArticles: [Article!]!
+ """
+ Get all read articles across all feeds
+ """
+ readArticles: [Article!]!
- """
- Get a specific feed by ID
- """
- feed(id: ID!): Feed
+ """
+ Get a specific feed by ID
+ """
+ feed(id: ID!): Feed
- """
- Get a specific article by ID
- """
- article(id: ID!): Article
+ """
+ Get a specific article by ID
+ """
+ article(id: ID!): Article
}
"""
Root mutation type for modifying data
"""
type Mutation {
- """
- Add a new feed subscription
- """
- addFeed(url: String!): Feed!
-
- """
- Unsubscribe from a feed (preserves feed and article data)
- """
- unsubscribeFeed(id: ID!): Boolean!
-
- """
- Mark an article as read
- """
- markArticleRead(id: ID!): Article!
-
- """
- Mark an article as unread
- """
- markArticleUnread(id: ID!): Article!
-
- """
- Mark all articles in a feed as read
- """
- markFeedRead(id: ID!): Feed!
-
- """
- Mark all articles in a feed as unread
- """
- markFeedUnread(id: ID!): Feed!
+ """
+ Add a new feed subscription
+ """
+ addFeed(url: String!): Feed!
+
+ """
+ Unsubscribe from a feed (preserves feed and article data)
+ """
+ unsubscribeFeed(id: ID!): Boolean!
+
+ """
+ Mark an article as read
+ """
+ markArticleRead(id: ID!): Article!
+
+ """
+ Mark an article as unread
+ """
+ markArticleUnread(id: ID!): Article!
+
+ """
+ Mark all articles in a feed as read
+ """
+ markFeedRead(id: ID!): Feed!
+
+ """
+ Mark all articles in a feed as unread
+ """
+ markFeedUnread(id: ID!): Feed!
}
`, BuiltIn: false},
}
diff --git a/backend/main.go b/backend/main.go
index 150e0af..0efe66a 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -34,28 +34,14 @@ import (
var (
database *sql.DB
queries *db.Queries
- //go:embed static/*
- staticFS embed.FS
+ //go:embed public/*
+ publicFS embed.FS
+ //go:embed db/schema.sql
+ dbSchema string
)
func initDB(db *sql.DB) error {
- _, err := db.Exec(`
-CREATE TABLE IF NOT EXISTS feeds (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- url TEXT NOT NULL,
- title TEXT NOT NULL,
- fetched_at TEXT NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS articles (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- feed_id INTEGER NOT NULL,
- guid TEXT NOT NULL,
- title TEXT NOT NULL,
- url TEXT NOT NULL,
- is_read INTEGER NOT NULL
-);
-`)
+ _, err := db.Exec(dbSchema)
return err
}
@@ -186,7 +172,11 @@ func main() {
e.Use(middleware.Recover())
e.Use(middleware.CORS())
- e.GET("/static/*", echo.WrapHandler(http.FileServer(http.FS(staticFS))))
+ e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
+ HTML5: true,
+ Root: "public",
+ Filesystem: http.FS(publicFS),
+ }))
// Setup GraphQL server
srv := handler.New(graphql.NewExecutableSchema(graphql.Config{Resolvers: &resolver.Resolver{DB: database, Queries: queries}}))
diff --git a/backend/public/dummy.txt b/backend/public/dummy.txt
new file mode 100644
index 0000000..1c5c535
--- /dev/null
+++ b/backend/public/dummy.txt
@@ -0,0 +1,4 @@
+The public directory stores the build artifacts of the frontend.
+However, in the local environment, the frontend side runs a development server to serve those files, so this public directory is not used.
+Go does not allow specifying an empty directory with go:embed, so we place a dummy file here.
+This dummy file is not used in production builds.
diff --git a/common/graphql/schema.graphql b/common/graphql/schema.graphql
index 75d4d77..a0bfa7e 100644
--- a/common/graphql/schema.graphql
+++ b/common/graphql/schema.graphql
@@ -4,138 +4,138 @@ scalar DateTime
Represents a feed subscription in the system
"""
type Feed {
- """
- Unique identifier for the feed
- """
- id: ID!
-
- """
- URL of the RSS/Atom feed
- """
- url: String!
-
- """
- Title of the feed (extracted from feed metadata)
- """
- title: String!
-
- """
- Timestamp when the feed was last fetched
- """
- fetchedAt: DateTime!
-
- """
- Whether the user is currently subscribed to this feed
- """
- isSubscribed: Boolean!
-
- """
- Articles belonging to this feed
- """
- articles: [Article!]!
+ """
+ Unique identifier for the feed
+ """
+ id: ID!
+
+ """
+ URL of the RSS/Atom feed
+ """
+ url: String!
+
+ """
+ Title of the feed (extracted from feed metadata)
+ """
+ title: String!
+
+ """
+ Timestamp when the feed was last fetched
+ """
+ fetchedAt: DateTime!
+
+ """
+ Whether the user is currently subscribed to this feed
+ """
+ isSubscribed: Boolean!
+
+ """
+ Articles belonging to this feed
+ """
+ articles: [Article!]!
}
"""
Represents an individual article/post from a feed
"""
type Article {
- """
- Unique identifier for the article
- """
- id: ID!
-
- """
- ID of the feed this article belongs to
- """
- feedId: ID!
-
- """
- GUID from the RSS/Atom feed (unique identifier from feed)
- """
- guid: String!
-
- """
- Title of the article
- """
- title: String!
-
- """
- URL/link to the original article
- """
- url: String!
-
- """
- Whether the article has been marked as read
- """
- isRead: Boolean!
-
- """
- The feed this article belongs to
- """
- feed: Feed!
+ """
+ Unique identifier for the article
+ """
+ id: ID!
+
+ """
+ ID of the feed this article belongs to
+ """
+ feedId: ID!
+
+ """
+ GUID from the RSS/Atom feed (unique identifier from feed)
+ """
+ guid: String!
+
+ """
+ Title of the article
+ """
+ title: String!
+
+ """
+ URL/link to the original article
+ """
+ url: String!
+
+ """
+ Whether the article has been marked as read
+ """
+ isRead: Boolean!
+
+ """
+ The feed this article belongs to
+ """
+ feed: Feed!
}
"""
Root query type for reading data
"""
type Query {
- """
- Get all feeds with their metadata
- """
- feeds: [Feed!]!
-
- """
- Get all unread articles across all feeds
- """
- unreadArticles: [Article!]!
-
- """
- Get all read articles across all feeds
- """
- readArticles: [Article!]!
-
- """
- Get a specific feed by ID
- """
- feed(id: ID!): Feed
-
- """
- Get a specific article by ID
- """
- article(id: ID!): Article
+ """
+ Get all feeds with their metadata
+ """
+ feeds: [Feed!]!
+
+ """
+ Get all unread articles across all feeds
+ """
+ unreadArticles: [Article!]!
+
+ """
+ Get all read articles across all feeds
+ """
+ readArticles: [Article!]!
+
+ """
+ Get a specific feed by ID
+ """
+ feed(id: ID!): Feed
+
+ """
+ Get a specific article by ID
+ """
+ article(id: ID!): Article
}
"""
Root mutation type for modifying data
"""
type Mutation {
- """
- Add a new feed subscription
- """
- addFeed(url: String!): Feed!
-
- """
- Unsubscribe from a feed (preserves feed and article data)
- """
- unsubscribeFeed(id: ID!): Boolean!
-
- """
- Mark an article as read
- """
- markArticleRead(id: ID!): Article!
-
- """
- Mark an article as unread
- """
- markArticleUnread(id: ID!): Article!
-
- """
- Mark all articles in a feed as read
- """
- markFeedRead(id: ID!): Feed!
-
- """
- Mark all articles in a feed as unread
- """
- markFeedUnread(id: ID!): Feed!
+ """
+ Add a new feed subscription
+ """
+ addFeed(url: String!): Feed!
+
+ """
+ Unsubscribe from a feed (preserves feed and article data)
+ """
+ unsubscribeFeed(id: ID!): Boolean!
+
+ """
+ Mark an article as read
+ """
+ markArticleRead(id: ID!): Article!
+
+ """
+ Mark an article as unread
+ """
+ markArticleUnread(id: ID!): Article!
+
+ """
+ Mark all articles in a feed as read
+ """
+ markFeedRead(id: ID!): Feed!
+
+ """
+ Mark all articles in a feed as unread
+ """
+ markFeedUnread(id: ID!): Feed!
}
diff --git a/compose.yaml b/compose.yaml
index b4e0dc7..69dad6f 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -3,7 +3,7 @@ services:
build:
context: .
volumes:
- - ./feedaka.db:/app/feedaka.db
+ - ./data/feedaka.db:/app/feedaka.db
ports:
- '127.0.0.1:8002:8080'
environment:
diff --git a/frontend/graphql-codegen.ts b/frontend/graphql-codegen.ts
index af62625..d5339a4 100644
--- a/frontend/graphql-codegen.ts
+++ b/frontend/graphql-codegen.ts
@@ -2,8 +2,8 @@ import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
overwrite: true,
- schema: "../common/graphql/schema.graphql",
- documents: ["src/**/*.tsx", "src/**/*.ts", "src/**/*.graphql"],
+ schema: "src/graphql/schema.graphql",
+ documents: ["src/graphql/*.graphql"],
generates: {
"src/graphql/generated/": {
preset: "client",
diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg
deleted file mode 100644
index e7b8dfb..0000000
--- a/frontend/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg> \ No newline at end of file
diff --git a/frontend/src/graphql/schema.graphql b/frontend/src/graphql/schema.graphql
new file mode 120000
index 0000000..4271904
--- /dev/null
+++ b/frontend/src/graphql/schema.graphql
@@ -0,0 +1 @@
+../../../common/graphql/schema.graphql \ No newline at end of file