Compare commits

...

10 commits

14 changed files with 153 additions and 12 deletions

View file

@ -1,6 +1,10 @@
# Code Snippets
App to store my code snippets.
Backend application to store my code snippets and make them available via REST API.
[CLI CMS Companion Project](https://git.sr.ht/~travisshears/code-snippets-cli-cms)
This project is written in [Clojure](https://clojure.org/) and data is stored in [XTDB](https://xtdb.dev/).
## Links
@ -16,3 +20,12 @@ App to store my code snippets.
```
$ clojure -M -m snippets.infra.api
```
### How to create docker image
At this point I'm using a private AWS ECR repository for the project and deploying it in my homelab.
```shell
$ export AWS_PROFILE=personal
$ docker buildx build --platform linux/amd64,linux/arm64 -t 853019563312.dkr.ecr.eu-central-1.amazonaws.com/snippets-homelabstack:latest --push .
```

View file

@ -5,13 +5,13 @@ meta {
}
delete {
url: {{host}}/api/snippet?id=90d00a80-78c4-4bc9-a066-01c988599d05
url: {{host}}/api/snippet?id=d77d3463-c76e-4c53-a1d5-ecaf16c6c54e
body: none
auth: none
}
params:query {
id: 90d00a80-78c4-4bc9-a066-01c988599d05
id: d77d3463-c76e-4c53-a1d5-ecaf16c6c54e
}
body:json {

View file

@ -16,7 +16,7 @@ params:query {
body:json {
{
"title": "Updated from Bruno",
"tags": ["code", "mock"]
"title": "quick way to push last jj commit to git",
"tags": ["jj", "git"]
}
}

View file

@ -0,0 +1,23 @@
meta {
name: get_snippet_by_slug
type: http
seq: 10
}
get {
url: {{host}}/api/snippet-by-slug?slug=netcat-over-ping
body: none
auth: none
}
params:query {
slug: netcat-over-ping
}
body:json {
{
"title": "Test Snippet",
"markdown": "## Cool Snippet\ndoes a cool thing",
"tags": ["git", "jj"]
}
}

View file

@ -5,13 +5,13 @@ meta {
}
get {
url: {{host}}/api/snippets?limit=100&skip=0
url: {{host}}/api/snippets?limit=25&skip=0
body: none
auth: none
}
params:query {
limit: 100
limit: 25
skip: 0
}

View file

@ -0,0 +1,23 @@
meta {
name: get_tag
type: http
seq: 9
}
get {
url: {{host}}/api/tag?tag=git
body: none
auth: none
}
params:query {
tag: git
}
body:json {
{
"title": "Test Snippet",
"markdown": "## Cool Snippet\ndoes a cool thing",
"tags": ["git", "jj"]
}
}

View file

@ -0,0 +1,19 @@
meta {
name: get_tags
type: http
seq: 8
}
get {
url: {{host}}/api/tags
body: none
auth: none
}
body:json {
{
"title": "Test Snippet",
"markdown": "## Cool Snippet\ndoes a cool thing",
"tags": ["git", "jj"]
}
}

16
build.sh Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
set -e
export AWS_PROFILE=personal
export AWS_REGION=eu-central-1
REPO_NAME="snippets-homelabstack"
if ! aws ecr describe-repositories --repository-names "$REPO_NAME" >/dev/null 2>&1; then
aws ecr create-repository --repository-name "$REPO_NAME"
fi
docker buildx build --platform linux/amd64,linux/arm64 -t "853019563312.dkr.ecr.eu-central-1.amazonaws.com/${REPO_NAME}:latest" --push .
echo "Docker image built and pushed to AWS ECR"

View file

@ -1,7 +1,7 @@
{:paths ["src"]
:deps {ring/ring-core {:mvn/version "1.13.0"}
:deps {;; api
ring/ring-core {:mvn/version "1.13.0"}
ring/ring-jetty-adapter {:mvn/version "1.13.0"}
;; logging, required by jetty:
org.slf4j/slf4j-simple {:mvn/version "2.0.16"}
;; db

View file

@ -47,6 +47,26 @@
{:status 200
:body (format "Deleted snippet with id: %s if it existed" id)}))
(defn handle-view-tags [_args]
(let [tags (snippets.use-cases.view/view-tags)]
{:status 200
:body tags}))
(defn handle-view-snippets-by-tag [{params :query-params}]
(let [tag (get params "tag")]
{:status 200
:body (snippets.use-cases.view/view-snippets-by-tag tag)}))
(defn handle-view-tags [_]
(let [tags (snippets.use-cases.view/view-tags)]
{:status 200
:body tags}))
(defn handle-view-snippet-by-slug [{params :query-params}]
(let [slug (get params "slug")]
{:status 200
:body (snippets.use-cases.view/view-snippet-by-slug slug)}))
(defn wrap [handler id]
(fn [request]
(update (handler request) :wrap (fnil conj '()) id)))
@ -58,6 +78,9 @@
mm/wrap-format
[wrap :api]]}
["/ping" {:get handle-ping}]
["/tags" {:get handle-view-tags}]
["/tag" {:get handle-view-snippets-by-tag}]
["/snippet-by-slug" {:get handle-view-snippet-by-slug}]
["/snippets" {:get handle-view-snippets}]
["/snippet" {:post handle-create-snippet
:get handle-view-snippet

View file

@ -22,7 +22,10 @@
(format "(quote (-> (from :snippets [title pub-date tags slug markdown {:xt/id id}]) (order-by {:val pub-date, :dir :desc, :nulls :last}) (offset %s) (limit %s)))" skip limit)))))
(defn get-snippet-by-id [snippet-id]
(first (xt/q client ['#(from :snippets [{:xt/id %} slug title {:xt/id id} markdown pub-date]) snippet-id])))
(first (xt/q client ['#(from :snippets [{:xt/id %} slug title tags {:xt/id id} markdown pub-date]) snippet-id])))
(defn get-snippet-by-slug [slug]
(first (xt/q client ['#(from :snippets [{:xt/id id} {:slug %} slug title tags markdown pub-date]) slug])))
(defn put-snippet [id snippet]
(t/log! {:level :info, :data {:snippet snippet :id id}} "Saving new snippet to db")
@ -39,3 +42,12 @@
(defn patch-snippet [id patch]
(t/log! {:level :info, :data {:patch patch :id id}} "Patching snippet")
(xt/execute-tx client [[:patch-docs :snippets (merge {:xt/id id} patch)]]))
(defn list-tags []
;; (xt/q client '(-> (from :snippets [{:xt/id id} tags]) (unnest {:tag tags}) (without :tags) (aggregate tag {:ids (array-agg id)}))))
(xt/q client '(-> (from :snippets [{:xt/id id} tags]) (unnest {:tag tags}) (without :tags) (aggregate tag {:count (count id)}))))
(defn get-snippets-by-tag [tag]
(map #(dissoc % :tag)
(xt/q client (eval (read-string
(format "(quote (-> (from :snippets [title slug tags pub-date]) (unnest {:tag tags}) (without :tags) (where (= tag \"%s\"))))" tag))))))

View file

@ -1,5 +1,5 @@
(ns snippets.main
(:require [snippets.api :as api])
(:require [snippets.infra.api :as api])
(:gen-class))
(defn -main []

View file

@ -9,7 +9,7 @@
[:map {:closed true}
[:markdown {:optional true} :string]
[:title {:optional true} :string]
[:tags [:seqable :string]]
[:tags {:optional true} [:seqable :string]]
[:slug {:optional true} :string]]))
(defn edit-snippet [id patch]

View file

@ -9,3 +9,15 @@
(defn view-snippets [& args]
(db/list-snippets args))
(defn view-tags []
(t/log! {:level :info} "Viewing tags")
(db/list-tags))
(defn view-snippets-by-tag [tag]
(t/log! {:level :info :data {:tag tag}} "Viewing snippet by tag")
(db/get-snippets-by-tag tag))
(defn view-snippet-by-slug [slug]
(t/log! {:level :info :data {:slug slug}} "Viewing snippet by slug")
(db/get-snippet-by-slug slug))