├── public ├── robots.txt ├── 404-text.png ├── favicon.png ├── octostar.png ├── spaceship.png ├── guirlande1.png ├── guirlande2.png ├── guirlande3.png ├── octoshadow.png ├── favicon-dark.png ├── spaceship-shadow.png ├── light_theme_preview.svg ├── dark_theme_preview.svg ├── favicon-dark.svg ├── favicon.svg └── next.svg ├── .eslintrc.json ├── drizzle ├── 0074_dear_prowler.sql ├── 0001_living_morlocks.sql ├── 0002_clever_glorian.sql ├── 0007_certain_skrulls.sql ├── 0020_swift_exodus.sql ├── 0033_naive_landau.sql ├── 0032_cold_tattoo.sql ├── 0040_awesome_leech.sql ├── 0066_hot_quasimodo.sql ├── 0034_rainy_ezekiel.sql ├── 0039_marvelous_mercury.sql ├── 0009_lean_hammerhead.sql ├── 0022_early_rick_jones.sql ├── 0042_gorgeous_doctor_spectrum.sql ├── 0044_brainy_jazinda.sql ├── 0065_sad_dagger.sql ├── 0069_petite_shriek.sql ├── 0008_free_ghost_rider.sql ├── 0021_curly_captain_universe.sql ├── 0057_goofy_mephisto.sql ├── 0059_cuddly_grim_reaper.sql ├── 0062_naive_jubilee.sql ├── 0043_blue_tusk.sql ├── 0052_goofy_prism.sql ├── 0026_first_whizzer.sql ├── 0018_gifted_kate_bishop.sql ├── 0004_gifted_exodus.sql ├── 0003_zippy_black_tarantula.sql ├── 0005_pretty_crusher_hogan.sql ├── 0016_glorious_mariko_yashida.sql ├── 0031_ambiguous_wasp.sql ├── 0017_burly_nitro.sql ├── 0075_volatile_harry_osborn.sql ├── 0041_damp_jack_murdock.sql ├── 0050_special_blue_blade.sql ├── 0049_flowery_ikaris.sql ├── 0067_rapid_thunderbolt_ross.sql ├── 0070_even_princess_powerful.sql ├── 0068_wakeful_skaar.sql ├── 0028_needy_katie_power.sql ├── 0036_careful_freak.sql ├── 0047_same_stark_industries.sql ├── 0019_real_william_stryker.sql ├── 0024_reflective_zzzax.sql ├── 0045_bent_roulette.sql ├── 0014_tricky_scalphunter.sql ├── 0063_brief_colossus.sql ├── 0071_swift_captain_cross.sql ├── 0015_lucky_sentinels.sql ├── 0013_rainy_princess_powerful.sql ├── 0029_nasty_triathlon.sql ├── 0037_cynical_peter_parker.sql ├── 0035_deep_mephisto.sql ├── 0058_flimsy_ender_wiggin.sql ├── 0006_confused_the_watchers.sql ├── 0051_tricky_tempest.sql ├── 0023_bored_texas_twister.sql ├── 0012_brainy_invisible_woman.sql ├── 0027_spicy_meltdown.sql ├── 0056_kind_whirlwind.sql ├── 0073_sad_crusher_hogan.sql ├── 0061_striped_monster_badoon.sql ├── 0053_familiar_dragon_man.sql ├── 0054_marvelous_living_lightning.sql ├── 0055_nasty_toad_men.sql ├── 0060_wealthy_chimera.sql ├── 0010_nosy_toad.sql ├── 0025_mute_wilson_fisk.sql ├── 0064_faithful_ben_parker.sql ├── 0072_oval_fallen_one.sql ├── 0048_dusty_leper_queen.sql ├── 0046_square_ikaris.sql ├── 0038_common_hiroim.sql ├── 0030_yielding_blonde_phantom.sql └── 0011_mixed_falcon.sql ├── docker ├── Dockerfile.redis ├── docker-stack.prod.yaml ├── webdis.json ├── docker-stack.local.yaml └── Dockerfile.prod ├── pr-preview-workflow ├── bun.lockb ├── package.json ├── README.md ├── tsconfig.json ├── index.ts ├── add-caddyfile.ts └── add-docker-app.ts ├── src ├── app │ ├── (app) │ │ ├── @page_title │ │ │ ├── default.tsx │ │ │ ├── [user] │ │ │ │ ├── [repository] │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── [...pages] │ │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ ├── settings │ │ │ │ ├── page.tsx │ │ │ │ └── [...pages] │ │ │ │ │ └── page.tsx │ │ │ └── notifications │ │ │ │ └── page.tsx │ │ ├── @header_subnav │ │ │ ├── [user] │ │ │ │ ├── page.tsx │ │ │ │ └── [repository] │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── [...pages] │ │ │ │ │ └── page.tsx │ │ │ ├── default.tsx │ │ │ ├── settings │ │ │ │ ├── page.tsx │ │ │ │ └── [...pages] │ │ │ │ │ └── page.tsx │ │ │ └── notifications │ │ │ │ └── page.tsx │ │ ├── notifications │ │ │ └── page.tsx │ │ ├── settings │ │ │ ├── page.tsx │ │ │ ├── appearance │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── account │ │ │ │ └── page.tsx │ │ ├── [user] │ │ │ ├── page.tsx │ │ │ └── [repository] │ │ │ │ ├── layout.tsx │ │ │ │ ├── actions │ │ │ │ └── page.tsx │ │ │ │ └── issues │ │ │ │ └── new │ │ │ │ └── page.tsx │ │ └── layout.tsx │ ├── client-providers.tsx │ ├── global-error.tsx │ ├── layout.tsx │ └── not-found.tsx ├── lib │ ├── server │ │ ├── kv │ │ │ ├── sqlite │ │ │ │ ├── config.ts │ │ │ │ ├── kv-entry.sql.ts │ │ │ │ └── index.ts │ │ │ ├── index.server.ts │ │ │ ├── cloudfare.ts │ │ │ └── http.ts │ │ ├── db │ │ │ ├── schema │ │ │ │ ├── index.sql.ts │ │ │ │ ├── repository.sql.ts │ │ │ │ ├── mention.sql.ts │ │ │ │ ├── user.sql.ts │ │ │ │ └── label.sql.ts │ │ │ └── index.server.ts │ │ ├── rsc-utils.server.ts │ │ └── utils.server.ts │ ├── client │ │ ├── hooks │ │ │ ├── use-typed-params.ts │ │ │ ├── use-active-link.ts │ │ │ └── use-media-query.ts │ │ └── pauseable-timeout.ts │ └── shared │ │ ├── cache-keys.shared.ts │ │ └── constants.ts ├── models │ ├── dto │ │ ├── public-user-output-validator.ts │ │ ├── repository-output-validator.ts │ │ ├── update-profile-info-input-validator.ts │ │ └── issue-search-output-validator.ts │ ├── label.ts │ └── repository.ts ├── actions │ ├── markdown.action.tsx │ ├── middlewares.ts │ ├── user.action.ts │ └── theme.action.ts ├── components │ ├── card.tsx │ ├── badge.tsx │ ├── custom-rsc-renderer │ │ ├── rsc-manifest.ts │ │ ├── render-rsc-to-string.ts │ │ ├── load-client-references.ts │ │ └── rsc-client-renderer.tsx │ ├── issues │ │ ├── use-issue-author-list-query.ts │ │ ├── use-issue-author-list-by-name-query.ts │ │ ├── use-issue-label-list-query.ts │ │ ├── use-issue-assignee-list-query.ts │ │ ├── issue-row-skeleton.tsx │ │ ├── clear-search-button.tsx │ │ ├── issue-row-avatar-stack.tsx │ │ └── issue-search-link.tsx │ ├── user-dropdown │ │ ├── user-dropdown.server.tsx │ │ └── user-dropdown.client.tsx │ ├── markdown │ │ ├── markdown-error-boundary.tsx │ │ ├── markdown-title.tsx │ │ ├── markdown-skeleton.tsx │ │ ├── markdown-h.tsx │ │ └── markdown-code-block.tsx │ ├── skip-to-main-button.tsx │ ├── counter-badge.tsx │ ├── segmented-layout.tsx │ ├── skeleton.tsx │ ├── toast │ │ ├── toaster.server.tsx │ │ └── toaster.client.tsx │ ├── loading-indicator.tsx │ ├── avatar.tsx │ ├── react-query-provider.tsx │ ├── submit-button.tsx │ ├── tailwind-indicator.tsx │ ├── theme-form.tsx │ ├── x-mas-decorations.tsx │ ├── label-badge.tsx │ ├── icon-switcher.tsx │ ├── settings-vertical-navlist.tsx │ ├── cache.tsx │ ├── top-loader.tsx │ ├── footer.tsx │ ├── nav-link.tsx │ ├── markdown-editor │ │ └── markdown-editor-preview.tsx │ ├── vertical-nav-link.tsx │ ├── hovercard │ │ ├── user-hovercard-contents.tsx │ │ ├── hovercard.tsx │ │ └── issue-hovercard-link.tsx │ ├── tooltip.tsx │ ├── theme-card.tsx │ ├── header │ │ └── header-navlinks.tsx │ └── update-user-infos-form.tsx ├── env.ts └── env-config.mjs ├── .dockerignore ├── postcss.config.cjs ├── prettier.config.cjs ├── dc-build-local.sh ├── drizzle.config.ts ├── migrate.mjs ├── .env.example ├── .vscode └── settings.json ├── searching.md ├── ecosystem.config.cjs ├── biome.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── refactor.md │ └── bug_report.md └── pull_request_template.md ├── docker-compose.yaml ├── next.config.mjs ├── .gitignore ├── globals.d.ts ├── tsconfig.script.json ├── tsconfig.json ├── LICENSE ├── latency.ts ├── scripts └── fetchUsers.ts ├── rsdw.d.ts └── CONTRIBUTING.md /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /drizzle/0074_dear_prowler.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS "repo_name_idx"; -------------------------------------------------------------------------------- /public/404-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/404-text.png -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/favicon.png -------------------------------------------------------------------------------- /public/octostar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/octostar.png -------------------------------------------------------------------------------- /public/spaceship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/spaceship.png -------------------------------------------------------------------------------- /drizzle/0001_living_morlocks.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_users" ADD COLUMN "company" varchar(255); -------------------------------------------------------------------------------- /drizzle/0002_clever_glorian.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" RENAME COLUMN "description" TO "body"; -------------------------------------------------------------------------------- /drizzle/0007_certain_skrulls.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ALTER COLUMN "number" SET NOT NULL; -------------------------------------------------------------------------------- /drizzle/0020_swift_exodus.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "title_idx" ON "gh_next_issues" ("title"); -------------------------------------------------------------------------------- /drizzle/0033_naive_landau.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ALTER COLUMN "title" SET DATA TYPE text; -------------------------------------------------------------------------------- /public/guirlande1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/guirlande1.png -------------------------------------------------------------------------------- /public/guirlande2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/guirlande2.png -------------------------------------------------------------------------------- /public/guirlande3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/guirlande3.png -------------------------------------------------------------------------------- /public/octoshadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/octoshadow.png -------------------------------------------------------------------------------- /drizzle/0032_cold_tattoo.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ALTER COLUMN "repository_id" SET NOT NULL; -------------------------------------------------------------------------------- /public/favicon-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/favicon-dark.png -------------------------------------------------------------------------------- /docker/Dockerfile.redis: -------------------------------------------------------------------------------- 1 | FROM nicolas/webdis:0.1.22 2 | 3 | COPY ./docker/webdis.json /etc/webdis.prod.json -------------------------------------------------------------------------------- /drizzle/0040_awesome_leech.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ALTER COLUMN "title" SET DATA TYPE varchar(500); -------------------------------------------------------------------------------- /drizzle/0066_hot_quasimodo.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ADD COLUMN "author_username_cs" varchar(255); -------------------------------------------------------------------------------- /public/spaceship-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/public/spaceship-shadow.png -------------------------------------------------------------------------------- /drizzle/0034_rainy_ezekiel.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_repositories" ADD COLUMN "is_public" boolean DEFAULT true; -------------------------------------------------------------------------------- /drizzle/0039_marvelous_mercury.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "iss_status_idx" ON "gh_next_issues" ("status"); -------------------------------------------------------------------------------- /pr-preview-workflow/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fredkiss3/gh-next/HEAD/pr-preview-workflow/bun.lockb -------------------------------------------------------------------------------- /src/app/(app)/@page_title/default.tsx: -------------------------------------------------------------------------------- 1 | export default function DefaultPageTitle() { 2 | return null; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/(app)/@header_subnav/[user]/page.tsx: -------------------------------------------------------------------------------- 1 | export default function UserHeaderSubNav() { 2 | return null; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/(app)/@header_subnav/default.tsx: -------------------------------------------------------------------------------- 1 | export default function DefaultHeaderSubNav() { 2 | return null; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/(app)/notifications/page.tsx: -------------------------------------------------------------------------------- 1 | export default async function NotificationsPage() { 2 | return <>; 3 | } 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .env.local 3 | Dockerfile 4 | .dockerignore 5 | node_modules 6 | npm-debug.log 7 | README.md 8 | .git -------------------------------------------------------------------------------- /drizzle/0009_lean_hammerhead.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_labels" ADD CONSTRAINT "gh_next_labels_name_unique" UNIQUE("name"); -------------------------------------------------------------------------------- /drizzle/0022_early_rick_jones.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ADD COLUMN "updated_at" timestamp DEFAULT now() NOT NULL; -------------------------------------------------------------------------------- /drizzle/0042_gorgeous_doctor_spectrum.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "rakt_type_idx" ON "gh_next_reactions" ("author_id"); -------------------------------------------------------------------------------- /drizzle/0044_brainy_jazinda.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "ment_issue_idx" ON "gh_next_issue_user_mentions" ("issue_id"); -------------------------------------------------------------------------------- /drizzle/0065_sad_dagger.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues_to_assignees" ALTER COLUMN "assignee_username_cs" DROP NOT NULL; -------------------------------------------------------------------------------- /drizzle/0069_petite_shriek.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues_to_assignees" ADD COLUMN "assignee_username_cs" varchar(255); -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /drizzle/0008_free_ghost_rider.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ADD CONSTRAINT "gh_next_issues_number_unique" UNIQUE("number"); -------------------------------------------------------------------------------- /src/app/(app)/@header_subnav/settings/page.tsx: -------------------------------------------------------------------------------- 1 | export default function SettingsHeaderSubNav() { 2 | return null; 3 | } 4 | -------------------------------------------------------------------------------- /drizzle/0021_curly_captain_universe.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ADD COLUMN "status_updated_at" timestamp DEFAULT now() NOT NULL; -------------------------------------------------------------------------------- /drizzle/0057_goofy_mephisto.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "ev_initiator_uname_idx" ON "gh_next_issue_events" ("initiator_username"); -------------------------------------------------------------------------------- /src/app/(app)/@header_subnav/notifications/page.tsx: -------------------------------------------------------------------------------- 1 | export default function SettingsHeaderSubNav() { 2 | return null; 3 | } 4 | -------------------------------------------------------------------------------- /drizzle/0059_cuddly_grim_reaper.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "ev_assignee_uname_idx" ON "gh_next_issue_events" ("assignee_username"); -------------------------------------------------------------------------------- /src/app/(app)/@header_subnav/settings/[...pages]/page.tsx: -------------------------------------------------------------------------------- 1 | export default function SettingsHeaderSubNav() { 2 | return null; 3 | } 4 | -------------------------------------------------------------------------------- /drizzle/0062_naive_jubilee.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "is_2_ass_assignee_uname_idx" ON "gh_next_issues_to_assignees" ("assignee_username"); -------------------------------------------------------------------------------- /drizzle/0043_blue_tusk.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS "rakt_type_idx";--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "rakt_type_idx" ON "gh_next_reactions" ("type"); -------------------------------------------------------------------------------- /drizzle/0052_goofy_prism.sql: -------------------------------------------------------------------------------- 1 | -- Custom SQL migration file, put you code below! -- 2 | CREATE COLLATION IF NOT EXISTS ci (provider = 'icu', locale = 'en-US-u-ks-level2', deterministic = false); -------------------------------------------------------------------------------- /drizzle/0026_first_whizzer.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issue_user_mentions" ADD CONSTRAINT "gh_next_issue_user_mentions_username_issue_id_comment_id_unique" UNIQUE("username","issue_id","comment_id"); -------------------------------------------------------------------------------- /drizzle/0018_gifted_kate_bishop.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ADD COLUMN "lock_reason" "issue_lock_reason";--> statement-breakpoint 2 | ALTER TABLE "gh_next_issue_events" DROP COLUMN IF EXISTS "lock_reason"; -------------------------------------------------------------------------------- /drizzle/0004_gifted_exodus.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ADD COLUMN "author_username" varchar(255) NOT NULL;--> statement-breakpoint 2 | ALTER TABLE "gh_next_issues" ADD COLUMN "author_avatar_url" varchar(255) NOT NULL; -------------------------------------------------------------------------------- /drizzle/0003_zippy_black_tarantula.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" DROP CONSTRAINT "gh_next_issues_assignee_id_gh_next_users_id_fk"; 2 | --> statement-breakpoint 3 | ALTER TABLE "gh_next_issues" DROP COLUMN IF EXISTS "assignee_id"; -------------------------------------------------------------------------------- /drizzle/0005_pretty_crusher_hogan.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_comments" ADD COLUMN "author_username" varchar(255) NOT NULL;--> statement-breakpoint 2 | ALTER TABLE "gh_next_comments" ADD COLUMN "author_avatar_url" varchar(255) NOT NULL; -------------------------------------------------------------------------------- /drizzle/0016_glorious_mariko_yashida.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issue_events" ADD COLUMN "assignee_username" varchar(255);--> statement-breakpoint 2 | ALTER TABLE "gh_next_issue_events" ADD COLUMN "assignee_avatar_url" varchar(255); -------------------------------------------------------------------------------- /drizzle/0031_ambiguous_wasp.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" DROP CONSTRAINT "gh_next_issues_number_unique";--> statement-breakpoint 2 | ALTER TABLE "gh_next_issues" ADD CONSTRAINT "uniq_number_idx" UNIQUE("repository_id","number"); -------------------------------------------------------------------------------- /drizzle/0017_burly_nitro.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issue_events" DROP CONSTRAINT "gh_next_issue_events_assignee_id_gh_next_users_id_fk"; 2 | --> statement-breakpoint 3 | ALTER TABLE "gh_next_issue_events" DROP COLUMN IF EXISTS "assignee_id"; -------------------------------------------------------------------------------- /drizzle/0075_volatile_harry_osborn.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_repositories" ADD COLUMN "description" text DEFAULT '' NOT NULL; 2 | UPDATE "gh_next_repositories" SET "description" = 'A minimal Github clone built on nextjs app router.' WHERE 1=1; -------------------------------------------------------------------------------- /prettier.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import("prettier").Config} */ 2 | module.exports = { 3 | tabWidth: 2, 4 | semi: true, 5 | arrowParens: "always", 6 | // plugins: ["prettier-plugin-tailwindcss"], 7 | trailingComma: "none" 8 | }; 9 | -------------------------------------------------------------------------------- /dc-build-local.sh: -------------------------------------------------------------------------------- 1 | while read -r line; do 2 | build_args="$build_args --build-arg $line" 3 | done < .env.docker.local 4 | echo args="'$build_args'" 5 | docker buildx build -t dcr.fredkiss.dev/gh-next:dev -f docker/Dockerfile.dev $build_args . -------------------------------------------------------------------------------- /drizzle/0041_damp_jack_murdock.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "com_author_uname_idx" ON "gh_next_comments" ("author_username");--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "iss_author_uname_idx" ON "gh_next_issues" ("author_username"); -------------------------------------------------------------------------------- /src/app/(app)/@page_title/[user]/[repository]/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | // types 3 | import { RepositoryPageTitle } from "~/app/(app)/@page_title/[user]/[repository]/[...pages]/page"; 4 | 5 | export default RepositoryPageTitle; 6 | -------------------------------------------------------------------------------- /drizzle/0050_special_blue_blade.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "lbl_2_iss_issue_fk_index" ON "gh_next_labels_to_issues" ("issue_id");--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "lbl_2_iss_assignee_fk_index" ON "gh_next_labels_to_issues" ("label_id"); -------------------------------------------------------------------------------- /drizzle/0049_flowery_ikaris.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "is_2_ass_issue_fk_index" ON "gh_next_issues_to_assignees" ("issue_id");--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "is_2_ass_assignee_fk_index" ON "gh_next_issues_to_assignees" ("assignee_id"); -------------------------------------------------------------------------------- /drizzle/0067_rapid_thunderbolt_ross.sql: -------------------------------------------------------------------------------- 1 | 2 | -- copy data from the ci column to the cs column 3 | UPDATE "gh_next_issues" SET "author_username_cs" = "author_username"; 4 | 5 | ALTER TABLE "gh_next_issues" ALTER COLUMN "author_username_cs" SET NOT NULL; -- add the not-null constraint -------------------------------------------------------------------------------- /drizzle/0070_even_princess_powerful.sql: -------------------------------------------------------------------------------- 1 | -- ALTER TABLE "gh_next_issues_to_assignees" ADD COLUMN "assignee_username_cs" varchar(255); 2 | 3 | -- copy data from the ci column to the cs column 4 | UPDATE "gh_next_issues_to_assignees" SET "assignee_username_cs" = "assignee_username"; 5 | -------------------------------------------------------------------------------- /src/app/(app)/settings/page.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "next/navigation"; 2 | import { getUserOrRedirect } from "~/actions/auth.action"; 3 | 4 | export default async function Page() { 5 | await getUserOrRedirect("/settings/account"); 6 | redirect("/settings/account"); 7 | } 8 | -------------------------------------------------------------------------------- /drizzle/0068_wakeful_skaar.sql: -------------------------------------------------------------------------------- 1 | DROP TRIGGER IF EXISTS "username_update_trigger" ON "gh_next_issues_to_assignees"; 2 | DROP TRIGGER IF EXISTS "username_insert_trigger" ON "gh_next_issues_to_assignees"; 3 | ALTER TABLE "gh_next_issues_to_assignees" DROP COLUMN IF EXISTS "assignee_username_cs"; -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "drizzle-kit"; 2 | 3 | export default { 4 | schema: "./src/lib/server/db/schema/*.sql.ts", 5 | out: "./drizzle", 6 | driver: "pg", 7 | dbCredentials: { 8 | connectionString: process.env.DATABASE_URL! 9 | } 10 | } satisfies Config; 11 | -------------------------------------------------------------------------------- /drizzle/0028_needy_katie_power.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_comments" ADD COLUMN "content_search_vector" tsvector generated always as (to_tsvector('english',content)) stored;--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "content_search_vector_idex" ON "gh_next_comments" using gin("content_search_vector"); -------------------------------------------------------------------------------- /drizzle/0036_careful_freak.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "author_fk_idx" ON "gh_next_reactions" ("author_id");--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "issue_fk_idx" ON "gh_next_reactions" ("issue_id");--> statement-breakpoint 3 | CREATE INDEX IF NOT EXISTS "comment_fk_idx" ON "gh_next_reactions" ("comment_id"); -------------------------------------------------------------------------------- /drizzle/0047_same_stark_industries.sql: -------------------------------------------------------------------------------- 1 | -- Custom SQL migration file, put you code below! -- 2 | CREATE INDEX IF NOT EXISTS "comment_count_issue_id_idx" ON "comment_count_per_issue" ("issue_id");--> statement-breakpoint 3 | CREATE INDEX IF NOT EXISTS "reaction_count_issue_id_idx" ON "reaction_count_per_issue" ("issue_id"); 4 | -------------------------------------------------------------------------------- /drizzle/0019_real_william_stryker.sql: -------------------------------------------------------------------------------- 1 | ALTER TYPE event_type RENAME TO event_type_old; 2 | CREATE TYPE event_type AS ENUM('CHANGE_TITLE', 'TOGGLE_STATUS', 'ISSUE_MENTION', 'ASSIGN_USER', 'ADD_LABEL', 'REMOVE_LABEL', 'ADD_COMMENT'); 3 | ALTER TABLE gh_next_issue_events ALTER COLUMN type TYPE event_type USING type::text::event_type; -------------------------------------------------------------------------------- /pr-preview-workflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preview-workflow", 3 | "module": "index.ts", 4 | "type": "module", 5 | "devDependencies": { 6 | "@types/bun": "latest" 7 | }, 8 | "peerDependencies": { 9 | "typescript": "^5.0.0" 10 | }, 11 | "dependencies": { 12 | "zod": "^3.22.4" 13 | } 14 | } -------------------------------------------------------------------------------- /pr-preview-workflow/README.md: -------------------------------------------------------------------------------- 1 | # preview-workflow 2 | 3 | To install dependencies: 4 | 5 | ```bash 6 | bun install 7 | ``` 8 | 9 | To run: 10 | 11 | ```bash 12 | bun run index.ts 13 | ``` 14 | 15 | This project was created using `bun init` in bun v1.0.25. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. 16 | -------------------------------------------------------------------------------- /src/app/(app)/[user]/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type { PageProps } from "~/lib/types"; 3 | 4 | export default async function UserPage({ 5 | params: { username } 6 | }: PageProps<{ username: string }>) { 7 | return ( 8 | <> 9 |

User {username}

10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/lib/server/kv/sqlite/config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "drizzle-kit"; 2 | 3 | export default ({ 4 | schema: "./src/lib/kv/sqlite/kv-entry.sql.ts", 5 | driver: "turso", 6 | dbCredentials: { 7 | url: process.env.TURSO_DB_URL!, 8 | authToken: process.env.TURSO_DB_TOKEN! 9 | } 10 | } satisfies Config); 11 | -------------------------------------------------------------------------------- /drizzle/0024_reflective_zzzax.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS "content_search_vector_idex";--> statement-breakpoint 2 | DROP INDEX IF EXISTS "body_search_vector_idex";--> statement-breakpoint 3 | ALTER TABLE "gh_next_comments" DROP COLUMN IF EXISTS "content_search_vector";--> statement-breakpoint 4 | ALTER TABLE "gh_next_issues" DROP COLUMN IF EXISTS "body_search_vector"; -------------------------------------------------------------------------------- /src/app/client-providers.tsx: -------------------------------------------------------------------------------- 1 | // app/provider.tsx 2 | "use client"; 3 | import * as React from "react"; 4 | // components 5 | import { ReactQueryProvider } from "~/components/react-query-provider"; 6 | 7 | export function ClientProviders({ children }: { children: React.ReactNode }) { 8 | return {children}; 9 | } 10 | -------------------------------------------------------------------------------- /drizzle/0045_bent_roulette.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS "title_idx";--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "iss_title_idx" ON "gh_next_issues" ("title");--> statement-breakpoint 3 | CREATE INDEX IF NOT EXISTS "iss_created_idx" ON "gh_next_issues" ("created_at");--> statement-breakpoint 4 | CREATE INDEX IF NOT EXISTS "iss_updated_idx" ON "gh_next_issues" ("updated_at"); -------------------------------------------------------------------------------- /drizzle/0014_tricky_scalphunter.sql: -------------------------------------------------------------------------------- 1 | DO $$ BEGIN 2 | CREATE TYPE "issue_lock_reason" AS ENUM('OFF_TOPIC', 'TOO_HEATED', 'RESOLVED', 'SPAM'); 3 | EXCEPTION 4 | WHEN duplicate_object THEN null; 5 | END $$; 6 | --> statement-breakpoint 7 | ALTER TYPE "event_type" ADD VALUE 'ISSUE_LOCK';--> statement-breakpoint 8 | ALTER TABLE "gh_next_issue_events" ADD COLUMN "lock_reason" "issue_lock_reason"; -------------------------------------------------------------------------------- /drizzle/0063_brief_colossus.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues_to_assignees" ADD COLUMN "assignee_username_cs" varchar(255); 2 | 3 | -- copy data from the ci column to the cs column 4 | UPDATE "gh_next_issues_to_assignees" SET "assignee_username_cs" = "assignee_username"; 5 | 6 | ALTER TABLE "gh_next_issues_to_assignees" ALTER COLUMN "assignee_username_cs" SET NOT NULL; -- add the not-null constraint -------------------------------------------------------------------------------- /migrate.mjs: -------------------------------------------------------------------------------- 1 | import { migrate } from "drizzle-orm/postgres-js/migrator"; 2 | import postgres from "postgres"; 3 | import { drizzle } from "drizzle-orm/postgres-js"; 4 | 5 | const db = drizzle( 6 | postgres(process.env.REMOTE_DATABASE_URL ?? process.env.DATABASE_URL) 7 | ); 8 | 9 | async function main() { 10 | await migrate(db, { migrationsFolder: "drizzle" }); 11 | process.exit(0); 12 | } 13 | main(); 14 | -------------------------------------------------------------------------------- /drizzle/0071_swift_captain_cross.sql: -------------------------------------------------------------------------------- 1 | -- Custom SQL migration file, put you code below! -- 2 | CREATE TRIGGER iss_2_ass_assignee_username_update_trigger 3 | BEFORE UPDATE ON gh_next_issues_to_assignees 4 | FOR EACH ROW EXECUTE FUNCTION sync_username_update(); 5 | 6 | CREATE TRIGGER iss_2_ass_assignee_username_insert_trigger 7 | BEFORE INSERT ON gh_next_issues_to_assignees 8 | FOR EACH ROW EXECUTE FUNCTION sync_username_insert(); -------------------------------------------------------------------------------- /src/app/(app)/@header_subnav/[user]/[repository]/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { HeaderNavLinks } from "~/components/header/header-navlinks"; 3 | 4 | import type { PageProps } from "~/lib/types"; 5 | 6 | export default function RepositoryHeaderSubnav({ 7 | params 8 | }: PageProps<{ 9 | user: string; 10 | repository: string; 11 | }>) { 12 | return ; 13 | } 14 | -------------------------------------------------------------------------------- /drizzle/0015_lucky_sentinels.sql: -------------------------------------------------------------------------------- 1 | DO $$ BEGIN 2 | CREATE TYPE "comment_hide_reason" AS ENUM('ABUSE', 'OFF_TOPIC', 'OUTDATED', 'RESOLVED', 'DUPLICATE', 'SPAM'); 3 | EXCEPTION 4 | WHEN duplicate_object THEN null; 5 | END $$; 6 | --> statement-breakpoint 7 | ALTER TABLE "gh_next_comments" ADD COLUMN "hidden" boolean DEFAULT false NOT NULL;--> statement-breakpoint 8 | ALTER TABLE "gh_next_comments" ADD COLUMN "hidden_reason" "comment_hide_reason"; -------------------------------------------------------------------------------- /src/app/(app)/@header_subnav/[user]/[repository]/[...pages]/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { HeaderNavLinks } from "~/components/header/header-navlinks"; 3 | 4 | import type { PageProps } from "~/lib/types"; 5 | 6 | export default function RepositoryHeaderSubnav({ 7 | params 8 | }: PageProps<{ 9 | user: string; 10 | repository: string; 11 | }>) { 12 | return ; 13 | } 14 | -------------------------------------------------------------------------------- /src/models/dto/public-user-output-validator.ts: -------------------------------------------------------------------------------- 1 | import "server-only"; 2 | import { z } from "zod"; 3 | 4 | export const publicUserOutputValidator = z.object({ 5 | id: z.number().nullable(), 6 | username: z.string(), 7 | avatar_url: z.string(), 8 | bio: z.string().nullable(), 9 | location: z.string().nullable(), 10 | name: z.string().nullable() 11 | }); 12 | 13 | export type PublicUser = z.TypeOf; 14 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | SESSION_SECRET="" 2 | GITHUB_CLIENT_ID= 3 | GITHUB_REDIRECT_URI="http://localhost:3000/api/auth/callback" 4 | GITHUB_SECRET= 5 | GITHUB_PERSONAL_ACCESS_TOKEN="" 6 | REDIS_HTTP_URL="http://127.0.0.1:6380" 7 | REDIS_HTTP_USERNAME="user" 8 | REDIS_HTTP_PASSWORD="password" 9 | NEXT_PUBLIC_VERCEL_URL="localhost:3000" 10 | DATABASE_URL="postgresql://postgres:password@localhost:5433/gh_next" 11 | KV_PREFIX= -------------------------------------------------------------------------------- /src/actions/markdown.action.tsx: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { Markdown } from "~/components/markdown/markdown"; 4 | import { renderRSCtoString } from "~/components/custom-rsc-renderer/render-rsc-to-string"; 5 | 6 | export async function getMarkdownPreview( 7 | content: string, 8 | repositoryPath: `${string}/${string}` 9 | ) { 10 | return await renderRSCtoString( 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /drizzle/0013_rainy_princess_powerful.sql: -------------------------------------------------------------------------------- 1 | ALTER TYPE "event_type" ADD VALUE 'ADD_COMMENT';--> statement-breakpoint 2 | ALTER TABLE "gh_next_issue_events" ADD COLUMN "comment_id" integer;--> statement-breakpoint 3 | DO $$ BEGIN 4 | ALTER TABLE "gh_next_issue_events" ADD CONSTRAINT "gh_next_issue_events_comment_id_gh_next_comments_id_fk" FOREIGN KEY ("comment_id") REFERENCES "gh_next_comments"("id") ON DELETE cascade ON UPDATE no action; 5 | EXCEPTION 6 | WHEN duplicate_object THEN null; 7 | END $$; 8 | -------------------------------------------------------------------------------- /src/models/dto/repository-output-validator.ts: -------------------------------------------------------------------------------- 1 | import "server-only"; 2 | import { z } from "zod"; 3 | import { publicUserOutputValidator } from "~/models/dto/public-user-output-validator"; 4 | 5 | export const repositoryOutputValidator = z.object({ 6 | id: z.number(), 7 | name: z.string(), 8 | description: z.string(), 9 | is_public: z.boolean(), 10 | owner: publicUserOutputValidator 11 | }); 12 | 13 | export type RepositoryOutput = z.TypeOf; 14 | -------------------------------------------------------------------------------- /src/components/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { clsx } from "~/lib/shared/utils.shared"; 3 | 4 | export type CardProps = { 5 | className?: string; 6 | children?: React.ReactNode; 7 | }; 8 | 9 | export function Card({ className, children }: CardProps) { 10 | return ( 11 |
17 | {children} 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/server/db/schema/index.sql.ts: -------------------------------------------------------------------------------- 1 | import { customType, pgTableCreator } from "drizzle-orm/pg-core"; 2 | 3 | export const pgTable = pgTableCreator((name) => `gh_next_${name}`); 4 | 5 | export const tsVector = customType<{ 6 | data: string; 7 | config: { generated: string }; 8 | }>({ 9 | dataType(config) { 10 | return ( 11 | `tsvector` + 12 | (config 13 | ? ` generated always as (${config.generated}) stored` 14 | : "" 15 | ).toString() 16 | ); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/lib/server/kv/sqlite/kv-entry.sql.ts: -------------------------------------------------------------------------------- 1 | import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; 2 | import { sql } from "drizzle-orm"; 3 | import type { InferModel } from "drizzle-orm"; 4 | 5 | export const kvEntry = sqliteTable("kv_entries", { 6 | key: text("key").primaryKey(), 7 | value: text("value").notNull(), 8 | expiry: integer("expiry", { mode: "timestamp" }).default( 9 | sql`(strftime('%s', 'now'))` 10 | ) 11 | }); 12 | 13 | export type KVEntry = InferModel; 14 | -------------------------------------------------------------------------------- /drizzle/0029_nasty_triathlon.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS "content_search_vector_idex";--> statement-breakpoint 2 | DROP INDEX IF EXISTS "body_search_vector_idx";--> statement-breakpoint 3 | DROP INDEX IF EXISTS "title_search_vector_idx";--> statement-breakpoint 4 | ALTER TABLE "gh_next_comments" DROP COLUMN IF EXISTS "content_search_vector";--> statement-breakpoint 5 | ALTER TABLE "gh_next_issues" DROP COLUMN IF EXISTS "body_search_vector";--> statement-breakpoint 6 | ALTER TABLE "gh_next_issues" DROP COLUMN IF EXISTS "title_search_vector"; -------------------------------------------------------------------------------- /src/components/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { clsx } from "~/lib/shared/utils.shared"; 3 | 4 | export type BadgeProps = { 5 | label: string; 6 | className?: string; 7 | }; 8 | 9 | export function Badge({ label, className }: BadgeProps) { 10 | return ( 11 | 17 | {label} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /drizzle/0037_cynical_peter_parker.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS "author_fk_idx";--> statement-breakpoint 2 | DROP INDEX IF EXISTS "issue_fk_idx";--> statement-breakpoint 3 | DROP INDEX IF EXISTS "comment_fk_idx";--> statement-breakpoint 4 | CREATE INDEX IF NOT EXISTS "rakt_author_fk_idx" ON "gh_next_reactions" ("author_id");--> statement-breakpoint 5 | CREATE INDEX IF NOT EXISTS "rakt_issue_fk_idx" ON "gh_next_reactions" ("issue_id");--> statement-breakpoint 6 | CREATE INDEX IF NOT EXISTS "rakt_comment_fk_idx" ON "gh_next_reactions" ("comment_id"); -------------------------------------------------------------------------------- /src/app/(app)/@page_title/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Link from "next/link"; 3 | import { clsx } from "~/lib/shared/utils.shared"; 4 | 5 | export default function ExplorePageTitle() { 6 | return ( 7 | 16 | Explore 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /drizzle/0035_deep_mephisto.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX IF NOT EXISTS "author_fk_idx" ON "gh_next_comments" ("author_id");--> statement-breakpoint 2 | CREATE INDEX IF NOT EXISTS "issue_fk_idx" ON "gh_next_comments" ("issue_id");--> statement-breakpoint 3 | CREATE INDEX IF NOT EXISTS "repository_fk_idx" ON "gh_next_issues" ("repository_id");--> statement-breakpoint 4 | CREATE INDEX IF NOT EXISTS "author_fk_idx" ON "gh_next_issues" ("author_id");--> statement-breakpoint 5 | CREATE INDEX IF NOT EXISTS "creator_fk_idx" ON "gh_next_repositories" ("creator_id"); -------------------------------------------------------------------------------- /src/components/custom-rsc-renderer/rsc-manifest.ts: -------------------------------------------------------------------------------- 1 | export async function getClientManifest() { 2 | let clientManifest: ClientManifest = {}; 3 | 4 | // we concatennate all the manifest for all pages 5 | if (globalThis.__RSC_MANIFEST) { 6 | const allManifests = Object.values(globalThis.__RSC_MANIFEST); 7 | for (const rscManifest of allManifests) { 8 | clientManifest = { 9 | ...clientManifest, 10 | ...rscManifest.clientModules 11 | }; 12 | } 13 | } 14 | return clientManifest; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/issues/use-issue-author-list-query.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import { filterIssueAuthorsByUsername } from "~/actions/issue.action"; 3 | 4 | export function useIssueAuthorListQuery({ 5 | name = "", 6 | enabled 7 | }: { 8 | name?: string; 9 | enabled: boolean; 10 | }) { 11 | return useQuery({ 12 | queryKey: ["ISSUE_AUTHOR_LIST", name], 13 | queryFn: () => 14 | filterIssueAuthorsByUsername(name).then((result) => result.promise), 15 | enabled 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/client/hooks/use-typed-params.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { useParams } from "next/navigation"; 3 | 4 | export function useTypedParams( 5 | schema: TSchema, 6 | errorMessage = "Error parsing params, you are maybe in the wrong route" 7 | ): z.output { 8 | const _params = useParams(); 9 | const res = schema.safeParse(_params); 10 | if (!res.success) { 11 | throw new Error(errorMessage, { cause: res.error.flatten().fieldErrors }); 12 | } 13 | return res.data; 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[javascript]": { 3 | "editor.defaultFormatter": "biomejs.biome" 4 | }, 5 | "[typescript]": { 6 | "editor.defaultFormatter": "biomejs.biome" 7 | }, 8 | "[typescriptreact]": { 9 | "editor.defaultFormatter": "biomejs.biome" 10 | }, 11 | "editor.defaultFormatter": "biomejs.biome", 12 | "typescript.preferences.importModuleSpecifier": "non-relative", 13 | "typescript.tsdk": "node_modules/typescript/lib", 14 | "[json]": { 15 | "editor.defaultFormatter": "biomejs.biome" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docker/docker-stack.prod.yaml: -------------------------------------------------------------------------------- 1 | version: "3.4" 2 | 3 | services: 4 | gh-next-prod: 5 | image: dcr.fredkiss.dev/gh-next:latest 6 | deploy: 7 | replicas: 2 8 | update_config: 9 | parallelism: 1 10 | delay: 5s 11 | order: start-first 12 | failure_action: rollback 13 | restart_policy: 14 | condition: on-failure 15 | delay: 5s 16 | max_attempts: 3 17 | window: 120s 18 | networks: 19 | - gh-next 20 | networks: 21 | gh-next: 22 | external: true 23 | -------------------------------------------------------------------------------- /searching.md: -------------------------------------------------------------------------------- 1 | ## SEARCH 2 | 3 | - https://remarkjs.github.io/react-markdown/ 4 | - https://github.com/remarkjs/remark-rehype 5 | - https://github.com/remarkjs/remark-github 6 | - https://unifiedjs.com/explore/package/rehype-raw/ 7 | - https://github.com/remarkjs/react-markdown/pull/682 8 | - https://github.com/remarkjs/react-markdown/blob/main/lib/index.js 9 | 10 | 11 | ### Debugging 12 | 13 | - https://github.com/vercel/next.js/issues/48748 14 | 15 | ### Learning 16 | 17 | - https://github.github.com/gfm/#disallowed-raw-html-extension- 18 | -------------------------------------------------------------------------------- /src/components/issues/use-issue-author-list-by-name-query.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import { filterIssueAuthorsByName } from "~/actions/issue.action"; 3 | 4 | export function useIssueAuthorListByNameQuery({ 5 | name = "" 6 | }: { 7 | name?: string; 8 | }) { 9 | return useQuery({ 10 | queryKey: ["ISSUE_AUTHOR_LIST_BY_NAME", name], 11 | queryFn: () => 12 | filterIssueAuthorsByName(name).then((result) => result.promise), 13 | placeholderData: (previousData) => previousData 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /src/components/user-dropdown/user-dropdown.server.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { getAuthedUser } from "~/actions/auth.action"; 3 | import { UserDropdown as UserDropdownClient } from "./user-dropdown.client"; 4 | 5 | export async function UserDropdown() { 6 | const user = await getAuthedUser(); 7 | if (!user) return null; 8 | 9 | return ( 10 | 11 | ); 12 | } 13 | 14 | export function preloadAuthedUser() { 15 | getAuthedUser(); 16 | } 17 | -------------------------------------------------------------------------------- /ecosystem.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps: [ 3 | { 4 | name: "gh-clone", 5 | script: ".next/standalone/server.js", 6 | time: true, 7 | instances: 2, 8 | autorestart: true, 9 | max_restarts: 5, 10 | exec_mode: "cluster_mode", 11 | watch: false, 12 | max_memory_restart: "1G", 13 | wait_ready: true, 14 | listen_timeout: 10_000, 15 | increment_var: "PORT", 16 | env: { 17 | PORT: 8892, 18 | HOSTNAME: "0.0.0.0" 19 | } 20 | } 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /drizzle/0058_flimsy_ender_wiggin.sql: -------------------------------------------------------------------------------- 1 | -- ADD A case insensitive COLUMN for the username 2 | ALTER TABLE "gh_next_issue_events" ADD COLUMN "assignee_username_ci" varchar(255) COLLATE "ci"; 3 | -- migrate data from the old column to the new column 4 | UPDATE "gh_next_issue_events" SET "assignee_username_ci" = "assignee_username"; 5 | 6 | ALTER TABLE "gh_next_issue_events" DROP COLUMN "assignee_username"; -- remove the old column 7 | ALTER TABLE "gh_next_issue_events" RENAME COLUMN "assignee_username_ci" TO "assignee_username"; -- rename the new column to the old column 8 | -------------------------------------------------------------------------------- /src/components/markdown/markdown-error-boundary.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ErrorBoundary } from "react-error-boundary"; 4 | 5 | type MarkdownErrorBoundaryProps = { 6 | children: React.ReactNode; 7 | }; 8 | 9 | // TODO : render a better error UI 10 | export async function MarkdownErrorBoundary({ 11 | children 12 | }: MarkdownErrorBoundaryProps) { 13 | return ( 14 | <>Error rendering markdown : {error}} 16 | > 17 | {children} 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /drizzle/0006_confused_the_watchers.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issue_revisions" DROP CONSTRAINT "gh_next_issue_revisions_revised_by_id_gh_next_users_id_fk"; 2 | --> statement-breakpoint 3 | ALTER TABLE "gh_next_issue_revisions" ADD COLUMN "revised_by_username" varchar(255) NOT NULL;--> statement-breakpoint 4 | ALTER TABLE "gh_next_issue_revisions" ADD COLUMN "revised_by_avatar_url" varchar(255) NOT NULL;--> statement-breakpoint 5 | ALTER TABLE "gh_next_issues" ADD COLUMN "number" integer;--> statement-breakpoint 6 | ALTER TABLE "gh_next_issue_revisions" DROP COLUMN IF EXISTS "revised_by_id"; -------------------------------------------------------------------------------- /src/components/issues/use-issue-label-list-query.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import { filterLabelsByName } from "~/actions/issue.action"; 3 | 4 | export function useIssueLabelListByNameQuery({ 5 | name = "", 6 | enabled = true 7 | }: { 8 | name?: string; 9 | enabled?: boolean; 10 | }) { 11 | return useQuery({ 12 | queryKey: ["ISSUE_LABEL_LIST_BY_NAME", name], 13 | queryFn: () => filterLabelsByName(name).then((result) => result.promise), 14 | enabled, 15 | placeholderData: (previousData) => previousData 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/skip-to-main-button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { clsx } from "~/lib/shared/utils.shared"; 3 | 4 | export function SkipToMainButton() { 5 | return ( 6 | 15 | Skip to content 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "linter": { 3 | "enabled": false 4 | }, 5 | "formatter": { 6 | "enabled": true, 7 | "formatWithErrors": true, 8 | "indentStyle": "tab", 9 | "indentWidth": 2, 10 | "lineWidth": 80, 11 | "ignore": [] 12 | }, 13 | "javascript": { 14 | "formatter": { 15 | "arrowParentheses": "always", 16 | "indentStyle": "space", 17 | "trailingComma": "none", 18 | "indentWidth": 2 19 | } 20 | }, 21 | "json": { 22 | "formatter": { 23 | "indentWidth": 2, 24 | "indentStyle": "space" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /pr-preview-workflow/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ESNext"], 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "moduleDetection": "force", 7 | "jsx": "react-jsx", 8 | "allowJs": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "verbatimModuleSyntax": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "skipLibCheck": true, 18 | "strict": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "forceConsistentCasingInFileNames": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/(app)/@page_title/settings/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Link from "next/link"; 3 | import { clsx } from "~/lib/shared/utils.shared"; 4 | 5 | export default function SettingPageTitle() { 6 | return ( 7 | 16 | Settings 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/app/(app)/@page_title/settings/[...pages]/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Link from "next/link"; 3 | import { clsx } from "~/lib/shared/utils.shared"; 4 | 5 | export default function SettingPageTitle() { 6 | return ( 7 | 16 | Settings 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /docker/webdis.json: -------------------------------------------------------------------------------- 1 | { 2 | "http_host": "0.0.0.0", 3 | "http_port": 7379, 4 | "redis_host": "127.0.0.1", 5 | "redis_port": 6379, 6 | 7 | "acl": [ 8 | { 9 | "disabled": ["*"] 10 | }, 11 | 12 | { 13 | "http_basic_auth": "user:password", 14 | "enabled": [ 15 | "GET", 16 | "SET", 17 | "DEL", 18 | "SETEX", 19 | "HSET", 20 | "HGETALL", 21 | "HGET", 22 | "SREM", 23 | "PING", 24 | "SADD", 25 | "SMEMBERS" 26 | ] 27 | } 28 | ], 29 | 30 | "verbosity": 4, 31 | "logfile": "/dev/stderr" 32 | } 33 | -------------------------------------------------------------------------------- /src/app/(app)/@page_title/notifications/page.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Link from "next/link"; 3 | import { clsx } from "~/lib/shared/utils.shared"; 4 | 5 | export default function SettingPageTitle() { 6 | return ( 7 | 16 | Notifications 17 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/server/kv/index.server.ts: -------------------------------------------------------------------------------- 1 | import "server-only"; 2 | import { WebdisKV } from "./webdis.server.mjs"; 3 | 4 | export interface KVStore { 5 | set = {}>( 6 | key: string, 7 | value: T, 8 | ttl_in_seconds?: number, 9 | key_prefix?: string 10 | ): Promise; 11 | get = {}>( 12 | key: string, 13 | key_prefix?: string 14 | ): Promise; 15 | delete(key: string, key_prefix?: string): Promise; 16 | } 17 | 18 | function getKV(): KVStore { 19 | return new WebdisKV(); 20 | } 21 | 22 | export const kv = getKV(); 23 | -------------------------------------------------------------------------------- /drizzle/0051_tricky_tempest.sql: -------------------------------------------------------------------------------- 1 | -- Custom SQL migration file, put you code below! -- 2 | -- Custom SQL migration file, put you code below! -- 3 | CREATE OR REPLACE FUNCTION refresh_comment_count_per_issue() 4 | RETURNS TRIGGER AS $$ 5 | BEGIN 6 | REFRESH MATERIALIZED VIEW comment_count_per_issue; 7 | RETURN NEW; 8 | END; 9 | $$ LANGUAGE plpgsql;--> statement-breakpoint 10 | 11 | CREATE OR REPLACE FUNCTION refresh_reaction_count_per_issue() 12 | RETURNS TRIGGER AS $$ 13 | BEGIN 14 | REFRESH MATERIALIZED VIEW reaction_count_per_issue; 15 | RETURN NEW; 16 | END; 17 | $$ LANGUAGE plpgsql;--> statement-breakpoint 18 | -------------------------------------------------------------------------------- /src/components/markdown/markdown-title.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { clsx } from "~/lib/shared/utils.shared"; 3 | 4 | // types 5 | export type MarkdownTitleProps = { 6 | title: string; 7 | className?: string; 8 | }; 9 | 10 | export async function MarkdownTitle({ title, className }: MarkdownTitleProps) { 11 | const parsed = title.replace( 12 | /`(.*?)`/g, 13 | '$1' 14 | ); 15 | 16 | return ( 17 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /drizzle/0023_bored_texas_twister.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_comments" ADD COLUMN "content_search_vector" "tsvector";--> statement-breakpoint 2 | ALTER TABLE "gh_next_issues" ADD COLUMN "body_search_vector" "tsvector";--> statement-breakpoint 3 | 4 | UPDATE gh_next_issues SET body_search_vector = to_tsvector('english', body); 5 | UPDATE gh_next_comments SET content_search_vector = to_tsvector('english', content); 6 | 7 | CREATE INDEX IF NOT EXISTS "content_search_vector_idex" ON "gh_next_comments" using gin("content_search_vector");--> statement-breakpoint 8 | CREATE INDEX IF NOT EXISTS "body_search_vector_idex" ON "gh_next_issues" using gin("body_search_vector"); -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[✨ feature] i want this" 5 | labels: feature, need-triage 6 | assignees: "" 7 | --- 8 | 9 | ## Is your feature request related to a problem? Please describe. 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | ## Describe the solution you'd like 13 | A clear and concise description of what you want to happen. 14 | 15 | ## Additional context 16 | Add any other context or screenshots about the feature request here. 17 | 18 | - [ ] Are you willing to make a PR for this ? 19 | -------------------------------------------------------------------------------- /drizzle/0012_brainy_invisible_woman.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues_to_assignees" DROP CONSTRAINT "gh_next_issues_to_assignees_issue_id";--> statement-breakpoint 2 | ALTER TABLE "gh_next_issues_to_assignees" ADD COLUMN "id" serial NOT NULL;--> statement-breakpoint 3 | ALTER TABLE "gh_next_issues_to_assignees" ADD COLUMN "assignee_id" integer;--> statement-breakpoint 4 | DO $$ BEGIN 5 | ALTER TABLE "gh_next_issues_to_assignees" ADD CONSTRAINT "gh_next_issues_to_assignees_assignee_id_gh_next_users_id_fk" FOREIGN KEY ("assignee_id") REFERENCES "gh_next_users"("id") ON DELETE cascade ON UPDATE no action; 6 | EXCEPTION 7 | WHEN duplicate_object THEN null; 8 | END $$; 9 | -------------------------------------------------------------------------------- /drizzle/0027_spicy_meltdown.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE "gh_next_issues" ADD COLUMN "body_search_vector" tsvector generated always as (to_tsvector('english',body)) stored;--> statement-breakpoint 2 | ALTER TABLE "gh_next_issues" ADD COLUMN "title_search_vector" tsvector generated always as ( 3 | setweight(to_tsvector('simple',title), 'A') || ' ' || 4 | setweight(to_tsvector('english',title), 'B')) stored;--> statement-breakpoint 5 | CREATE INDEX IF NOT EXISTS "body_search_vector_idx" ON "gh_next_issues" using gin("body_search_vector");--> statement-breakpoint 6 | CREATE INDEX IF NOT EXISTS "title_search_vector_idx" ON "gh_next_issues" using gin("title_search_vector"); -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/refactor.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Refactor 3 | about: Suggest a new refactor task to the project 4 | title: "[♻️ refactor] We need to do..." 5 | labels: refactor, need-triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## What is the reason to do this refactor ?. 11 | A clear and concise description of why this refactor is needed and/or what advantages does it provide. 12 | 13 | ## Describe the work that needs to be done 14 | A clear and concise description of the work that needs to be done. 15 | 16 | ## Additional context 17 | Add any other context or screenshots about the issue here. 18 | 19 | - [ ] Are you willing to make a PR for this ? 20 | -------------------------------------------------------------------------------- /src/lib/client/hooks/use-active-link.ts: -------------------------------------------------------------------------------- 1 | import { usePathname } from "next/navigation"; 2 | import { linkWithSlash } from "~/lib/shared/utils.shared"; 3 | 4 | export function useActiveLink(href: string, isRoot?: boolean) { 5 | const path = usePathname(); 6 | 7 | // treat `/` as special or else it would always be considered active 8 | // as every path starts with `/` 9 | const url = new URL(href, "http://localhost"); 10 | if ( 11 | (url.pathname === "/" || isRoot) && 12 | linkWithSlash(url.pathname) !== linkWithSlash(path) 13 | ) { 14 | return false; 15 | } 16 | 17 | return linkWithSlash(path).startsWith(linkWithSlash(href)); 18 | } 19 | -------------------------------------------------------------------------------- /src/components/counter-badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export type CounterBadgeProps = { 4 | count?: number; 5 | }; 6 | 7 | export function CounterBadge({ count }: CounterBadgeProps) { 8 | return ( 9 | <> 10 | 20 | ({count ?? "undefined"}) 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/components/issues/use-issue-assignee-list-query.ts: -------------------------------------------------------------------------------- 1 | import { useQuery } from "@tanstack/react-query"; 2 | import { filterIssueAssignees } from "~/actions/issue.action"; 3 | 4 | export function useIssueAssigneeListQuery({ 5 | name = "", 6 | enabled, 7 | checkFullName 8 | }: { 9 | name?: string; 10 | enabled: boolean; 11 | checkFullName?: boolean; 12 | }) { 13 | return useQuery({ 14 | queryKey: ["ISSUE_ASSIGNEE_LIST", name], 15 | queryFn: () => 16 | filterIssueAssignees(name, checkFullName).then( 17 | (result) => result.promise 18 | ), 19 | enabled, 20 | placeholderData: (previousData) => previousData 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /drizzle/0056_kind_whirlwind.sql: -------------------------------------------------------------------------------- 1 | -- ADD A case insensitive COLUMN for the username 2 | ALTER TABLE "gh_next_issue_events" ADD COLUMN "initiator_username_ci" varchar(255) COLLATE "ci"; 3 | -- migrate data from the old column to the new column 4 | UPDATE "gh_next_issue_events" SET "initiator_username_ci" = "initiator_username"; 5 | 6 | ALTER TABLE "gh_next_issue_events" DROP COLUMN "initiator_username"; -- remove the old column 7 | ALTER TABLE "gh_next_issue_events" RENAME COLUMN "initiator_username_ci" TO "initiator_username"; -- rename the new column to the old column 8 | ALTER TABLE "gh_next_issue_events" ALTER COLUMN "initiator_username" SET NOT NULL; -- add the not-null constraint 9 | -------------------------------------------------------------------------------- /src/components/segmented-layout.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { clsx } from "~/lib/shared/utils.shared"; 3 | 4 | export type SegmentedLayoutProps = { 5 | children: React.ReactNode; 6 | className?: string; 7 | }; 8 | 9 | export function SegmentedLayout({ children, className }: SegmentedLayoutProps) { 10 | return ( 11 |
    li:not(:last-child)>*]:!border-r-0", 16 | "[&>li:not(:first-child)>*]:!rounded-l-none", 17 | "[&>li:not(:last-child)>*]:!rounded-r-none" 18 | )} 19 | > 20 | {children} 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | services: 4 | webdis: 5 | image: nicolas/webdis:latest 6 | volumes: # mount volume containing the config file 7 | - ./docker/webdis.json:/etc/webdis.prod.json 8 | ports: 9 | - "6380:7379" 10 | db: 11 | image: postgres:12-alpine 12 | restart: always 13 | volumes: 14 | - db-data:/var/lib/postgresql/data 15 | environment: 16 | POSTGRES_USER: postgres 17 | POSTGRES_PASSWORD: password 18 | POSTGRES_DB: gh_next 19 | ports: 20 | - "5433:5432" 21 | adminer: 22 | image: adminer 23 | restart: always 24 | ports: 25 | - 8081:8080 26 | 27 | volumes: 28 | db-data: 29 | -------------------------------------------------------------------------------- /drizzle/0073_sad_crusher_hogan.sql: -------------------------------------------------------------------------------- 1 | -- ADD A case insensitive COLUMN for the username 2 | ALTER TABLE "gh_next_repositories" ADD COLUMN "name_ci" varchar(255) COLLATE "ci"; 3 | -- migrate data from the old column to the new column 4 | UPDATE "gh_next_repositories" SET "name_ci" = "name"; 5 | 6 | ALTER TABLE "gh_next_repositories" DROP COLUMN "name"; -- remove the old column 7 | ALTER TABLE "gh_next_repositories" RENAME COLUMN "name_ci" TO "name"; -- rename the new column to the old column 8 | ALTER TABLE "gh_next_repositories" ALTER COLUMN "name" SET NOT NULL; -- add the not-null constraint 9 | 10 | CREATE UNIQUE INDEX "repo_name_uniq_idx" ON "gh_next_repositories" ("name"); -- readd the unique constraint-- -------------------------------------------------------------------------------- /drizzle/0061_striped_monster_badoon.sql: -------------------------------------------------------------------------------- 1 | -- ADD A case insensitive COLUMN for the username 2 | ALTER TABLE "gh_next_issues_to_assignees" ADD COLUMN "assignee_username_ci" varchar(255) COLLATE "ci"; 3 | -- migrate data from the old column to the new column 4 | UPDATE "gh_next_issues_to_assignees" SET "assignee_username_ci" = "assignee_username"; 5 | 6 | ALTER TABLE "gh_next_issues_to_assignees" DROP COLUMN "assignee_username"; -- remove the old column 7 | ALTER TABLE "gh_next_issues_to_assignees" RENAME COLUMN "assignee_username_ci" TO "assignee_username"; -- rename the new column to the old column 8 | ALTER TABLE "gh_next_issues_to_assignees" ALTER COLUMN "assignee_username" SET NOT NULL; -- add the not-null constraint 9 | -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import "./src/env-config.mjs"; 3 | 4 | /** @type {import('next').NextConfig} */ 5 | const nextConfig = { 6 | reactStrictMode: true, 7 | output: "standalone", 8 | cacheHandler: 9 | process.env.NODE_ENV === "production" 10 | ? "./custom-incremental-cache-handler.mjs" 11 | : undefined, 12 | cacheMaxMemorySize: 0, 13 | experimental: { 14 | taint: true 15 | }, 16 | logging: { 17 | fetches: { 18 | fullUrl: true 19 | } 20 | }, 21 | images: { 22 | remotePatterns: [ 23 | { 24 | protocol: "https", 25 | hostname: "avatars.githubusercontent.com" 26 | } 27 | ] 28 | } 29 | }; 30 | 31 | export default nextConfig; 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | 37 | 38 | # cloudfare pages 39 | .wrangler/ 40 | /cache/ 41 | .idea/ 42 | 43 | # Docker 44 | docker-stack.pr.yaml 45 | caddyfile.pr 46 | pr.caddyfile 47 | *.bak 48 | *.log 49 | notes.md 50 | certificates -------------------------------------------------------------------------------- /drizzle/0053_familiar_dragon_man.sql: -------------------------------------------------------------------------------- 1 | -- ADD A case insensitive COLUMN for the username 2 | ALTER TABLE "gh_next_issues" ADD COLUMN "author_username_ci" varchar(255) COLLATE "ci"; 3 | -- migrate data from the old column to the new column 4 | UPDATE "gh_next_issues" SET "author_username_ci" = "author_username"; 5 | 6 | ALTER TABLE "gh_next_issues" DROP COLUMN "author_username"; -- remove the old column 7 | ALTER TABLE "gh_next_issues" RENAME COLUMN "author_username_ci" TO "author_username"; -- rename the new column to the old column 8 | ALTER TABLE "gh_next_issues" ALTER COLUMN "author_username" SET NOT NULL; -- add the not-null constraint 9 | 10 | CREATE INDEX "iss_author_uname_idx" ON "gh_next_issues" ("author_username"); -- readd the index -------------------------------------------------------------------------------- /drizzle/0054_marvelous_living_lightning.sql: -------------------------------------------------------------------------------- 1 | -- ADD A case insensitive COLUMN for the username 2 | ALTER TABLE "gh_next_users" ADD COLUMN "username_ci" varchar(255) COLLATE "ci"; 3 | -- migrate data from the old column to the new column 4 | UPDATE "gh_next_users" SET "username_ci" = "username"; 5 | 6 | ALTER TABLE "gh_next_users" DROP COLUMN "username"; -- remove the old column 7 | ALTER TABLE "gh_next_users" RENAME COLUMN "username_ci" TO "username"; -- rename the new column to the old column 8 | ALTER TABLE "gh_next_users" ALTER COLUMN "username" SET NOT NULL; -- add the not-null constraint 9 | 10 | CREATE UNIQUE INDEX "uname_uniq_idx" ON "gh_next_users" ("username"); -- readd the index-- Custom SQL migration file, put you code below! -- -------------------------------------------------------------------------------- /drizzle/0055_nasty_toad_men.sql: -------------------------------------------------------------------------------- 1 | -- ADD A case insensitive COLUMN for the username 2 | ALTER TABLE "gh_next_comments" ADD COLUMN "author_username_ci" varchar(255) COLLATE "ci"; 3 | -- migrate data from the old column to the new column 4 | UPDATE "gh_next_comments" SET "author_username_ci" = "author_username"; 5 | 6 | ALTER TABLE "gh_next_comments" DROP COLUMN "author_username"; -- remove the old column 7 | ALTER TABLE "gh_next_comments" RENAME COLUMN "author_username_ci" TO "author_username"; -- rename the new column to the old column 8 | ALTER TABLE "gh_next_comments" ALTER COLUMN "author_username" SET NOT NULL; -- add the not-null constraint 9 | 10 | CREATE INDEX "com_author_uname_idx" ON "gh_next_comments" ("author_username"); -- readd the index -------------------------------------------------------------------------------- /src/app/(app)/layout.tsx: -------------------------------------------------------------------------------- 1 | // components 2 | import { Footer } from "~/components/footer"; 3 | import { Header } from "~/components/header/header"; 4 | import { clsx } from "~/lib/shared/utils.shared"; 5 | 6 | export default async function AppLayout({ 7 | children, 8 | header_subnav, 9 | page_title 10 | }: { 11 | children: React.ReactNode; 12 | header_subnav: React.ReactNode; 13 | page_title: React.ReactNode; 14 | }) { 15 | return ( 16 | <> 17 |
{header_subnav}
18 |
22 | {children} 23 |
24 |