Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ca6358e
add article CRUD endpoints
Hajbo Mar 17, 2025
b919017
Implement comments feature for articles
yamcodes Mar 18, 2025
5c4447f
improve names and repository
yamcodes Mar 18, 2025
813b6c8
Fix delete method in CommentsRepository to return the result of the d…
yamcodes Mar 18, 2025
557f2d2
Rename to slugify
Hajbo Mar 19, 2025
d3e679f
Add trailing slashes
Hajbo Mar 19, 2025
aba29d2
Return nulls if no article was found
Hajbo Mar 19, 2025
b7714e1
Update models & migrations
Hajbo Mar 19, 2025
c879e3a
Fix db:drop utility
Hajbo Mar 19, 2025
969bcc8
Specify error for optional authentication
Hajbo Mar 19, 2025
d811f95
Fix returned article count
Hajbo Mar 19, 2025
100453b
Fix feed by only returning articles from followed users, run bun fix
Hajbo Mar 19, 2025
e707eb6
Simplifiy update logic
Hajbo Mar 19, 2025
7c8a103
Merge branch 'main' into feature/articles-crud
Hajbo Mar 19, 2025
f90e537
Merge branch 'feature/articles-crud' into 78-implement-article-commen…
yamcodes Mar 19, 2025
08df554
edit taglist
yamcodes Mar 19, 2025
76b0ec8
fix more weird ai stuff
yamcodes Mar 19, 2025
4a70667
fix missing types
yamcodes Mar 19, 2025
b8a055a
fix DeleteArticleResponse schema
yamcodes Mar 19, 2025
23ec6aa
Add article CRUD endpoints and some others (#129)
Hajbo Mar 20, 2025
a6cc3fe
fix dates
yamcodes Mar 20, 2025
91447f6
Refactor comment author retrieval and add findByUserId method
yamcodes Mar 20, 2025
83ab1c4
Enhance comment retrieval with author details
yamcodes Mar 20, 2025
41ee814
Refactor comment author profile retrieval in CommentsService
yamcodes Mar 20, 2025
7f75dc5
Merge branch 'main' into 78-implement-article-comment-feature-2
yamcodes Mar 20, 2025
1cd3254
remove unused method
yamcodes Mar 20, 2025
f8e7faa
Enhance CommentsService and ProfilesService with improved type handli…
yamcodes Mar 20, 2025
18114bd
Refactor CommentsRepository and CommentsService for enhanced comment …
yamcodes Mar 21, 2025
29923e7
Simplify findById method in CommentsRepository
yamcodes Mar 21, 2025
dd9b96e
Update error handling in CommentsService to use NotFoundError
yamcodes Mar 21, 2025
581e9db
Update error handling in CommentsService to throw NotFoundError for m…
yamcodes Mar 21, 2025
3d964a3
Merge branch 'main' into 78-implement-article-comment-feature-2
yamcodes Mar 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,5 @@
"password": "postgres"
}
],
"cSpell.words": [
"bedstack",
"Elysia",
"elysiajs",
"favicons",
"typesafe"
]
"cSpell.words": ["bedstack", "Elysia", "elysiajs", "favicons", "typesafe"]
}
5 changes: 5 additions & 0 deletions db/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ export default defineConfig({
dialect: 'postgresql',
dbCredentials: dbCredentials,
strict: true,
// Redefine default migrations table and schema for the sake of clarity
migrations: {
table: '__drizzle_migrations',
schema: 'drizzle',
},
});
11 changes: 10 additions & 1 deletion db/drop.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { exit } from 'node:process';
import { db } from '@/database.providers';
import { articles, favoriteArticles } from '@articles/articles.model';
import dbConfig from '@db/config';
import { userFollows, users } from '@users/users.model';
import { getTableName, sql } from 'drizzle-orm';

const tables = [users, userFollows];
const tables = [userFollows, favoriteArticles, articles, users];
console.log('Dropping all tables from the database');

try {
Expand All @@ -17,6 +19,13 @@ try {
);
console.log(`Dropped ${name}`);
}
if (dbConfig.migrations?.table) {
// Clean up migrations
console.log('Dropping migrations table');
await tx.execute(
sql`DROP TABLE IF EXISTS ${sql.identifier(dbConfig.migrations.table)} CASCADE;`,
);
}
});

console.log('All tables dropped');
Expand Down
24 changes: 24 additions & 0 deletions db/migrations/0007_elite_patriot.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CREATE TABLE "articles" (
"id" serial PRIMARY KEY NOT NULL,
"slug" text NOT NULL,
"title" text NOT NULL,
"description" text NOT NULL,
"body" text NOT NULL,
"tag_list" text[] DEFAULT '{}'::text[] NOT NULL,
"created_at" timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
"updated_at" timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
"author_id" integer NOT NULL,
CONSTRAINT "articles_slug_unique" UNIQUE("slug")
);

CREATE TABLE "favorite_articles" (
"article_id" integer NOT NULL,
"user_id" integer NOT NULL,
"created_at" date DEFAULT CURRENT_DATE NOT NULL,
"updated_at" date DEFAULT CURRENT_DATE NOT NULL,
CONSTRAINT "favorite_articles_article_id_user_id_pk" PRIMARY KEY("article_id","user_id")
);

ALTER TABLE "articles" ADD CONSTRAINT "articles_author_id_users_id_fk" FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "favorite_articles" ADD CONSTRAINT "favorite_articles_article_id_articles_id_fk" FOREIGN KEY ("article_id") REFERENCES "public"."articles"("id") ON DELETE cascade ON UPDATE no action;
ALTER TABLE "favorite_articles" ADD CONSTRAINT "favorite_articles_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
307 changes: 307 additions & 0 deletions db/migrations/meta/0007_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
{
"id": "a387bcf4-fc25-4a7c-9467-3f76e9356261",
"prevId": "c621d843-d221-4084-a3e3-361c92f1c7bf",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.articles": {
"name": "articles",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"slug": {
"name": "slug",
"type": "text",
"primaryKey": false,
"notNull": true
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": true
},
"body": {
"name": "body",
"type": "text",
"primaryKey": false,
"notNull": true
},
"tag_list": {
"name": "tag_list",
"type": "text[]",
"primaryKey": false,
"notNull": true,
"default": "'{}'::text[]"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_TIMESTAMP"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_TIMESTAMP"
},
"author_id": {
"name": "author_id",
"type": "integer",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"articles_author_id_users_id_fk": {
"name": "articles_author_id_users_id_fk",
"tableFrom": "articles",
"tableTo": "users",
"columnsFrom": ["author_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"articles_slug_unique": {
"name": "articles_slug_unique",
"nullsNotDistinct": false,
"columns": ["slug"]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.favorite_articles": {
"name": "favorite_articles",
"schema": "",
"columns": {
"article_id": {
"name": "article_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "date",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_DATE"
},
"updated_at": {
"name": "updated_at",
"type": "date",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_DATE"
}
},
"indexes": {},
"foreignKeys": {
"favorite_articles_article_id_articles_id_fk": {
"name": "favorite_articles_article_id_articles_id_fk",
"tableFrom": "favorite_articles",
"tableTo": "articles",
"columnsFrom": ["article_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
"favorite_articles_user_id_users_id_fk": {
"name": "favorite_articles_user_id_users_id_fk",
"tableFrom": "favorite_articles",
"tableTo": "users",
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"favorite_articles_article_id_user_id_pk": {
"name": "favorite_articles_article_id_user_id_pk",
"columns": ["article_id", "user_id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.user_follows": {
"name": "user_follows",
"schema": "",
"columns": {
"followed_id": {
"name": "followed_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"follower_id": {
"name": "follower_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "date",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_DATE"
},
"updated_at": {
"name": "updated_at",
"type": "date",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_DATE"
}
},
"indexes": {},
"foreignKeys": {
"user_follows_followed_id_users_id_fk": {
"name": "user_follows_followed_id_users_id_fk",
"tableFrom": "user_follows",
"tableTo": "users",
"columnsFrom": ["followed_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
"user_follows_follower_id_users_id_fk": {
"name": "user_follows_follower_id_users_id_fk",
"tableFrom": "user_follows",
"tableTo": "users",
"columnsFrom": ["follower_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"user_follows_followed_id_follower_id_pk": {
"name": "user_follows_followed_id_follower_id_pk",
"columns": ["followed_id", "follower_id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.users": {
"name": "users",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"bio": {
"name": "bio",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "'https://api.realworld.io/images/smiley-cyrus.jpg'"
},
"password": {
"name": "password",
"type": "text",
"primaryKey": false,
"notNull": true
},
"username": {
"name": "username",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "date",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_DATE"
},
"updated_at": {
"name": "updated_at",
"type": "date",
"primaryKey": false,
"notNull": true,
"default": "CURRENT_DATE"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"users_email_unique": {
"name": "users_email_unique",
"nullsNotDistinct": false,
"columns": ["email"]
},
"users_username_unique": {
"name": "users_username_unique",
"nullsNotDistinct": false,
"columns": ["username"]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
7 changes: 7 additions & 0 deletions db/migrations/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
"when": 1742050755293,
"tag": "0006_goofy_diamondback",
"breakpoints": false
},
{
"idx": 7,
"version": "7",
"when": 1742230377894,
"tag": "0007_elite_patriot",
"breakpoints": false
}
]
}
Loading