diff --git a/README.md b/README.md
index af468ca..d4bc24b 100644
--- a/README.md
+++ b/README.md
@@ -10,9 +10,9 @@
Website
ยท
- Issues
- ยท
Support
+ ยท
+ Self-Hosting
+
+
+Try [Railway](https://railway.com?referralCode=techulus), a modern platform that makes deploying applications simple and fast. Railway provides excellent developer experience with features like automatic deployments, built-in databases, and seamless scaling.
+
## Getting Started ๐
### Requirements
diff --git a/SELF-HOSTING.md b/SELF-HOSTING.md
new file mode 100644
index 0000000..15db2a0
--- /dev/null
+++ b/SELF-HOSTING.md
@@ -0,0 +1,49 @@
+# Self-Hosting Changes.Page
+
+This guide will help you set up and deploy Changes.Page on your own infrastructure.
+
+## Database Setup
+
+You have two options for setting up the database:
+
+### Option 1: Local Supabase (Recommended for Development)
+
+Follow the official Supabase self-hosting guide using Docker:
+https://supabase.com/docs/guides/self-hosting/docker
+
+### Option 2: Supabase Cloud
+
+Create a new project at [supabase.com](https://supabase.com) and use the provided connection details.
+
+## Application Deployment
+
+The repository includes Docker Compose files for easy deployment of the applications.
+
+1. Ensure you have Docker and Docker Compose installed
+2. Set up your environment variables in the respective `.env` files (see `.env.example` files in `apps/web` and `apps/page`)
+3. Run the applications using Docker Compose:
+
+```sh
+docker-compose up -d
+```
+
+## Feature Limitations
+
+Please note the following limitations when self-hosting:
+
+- **Billing**: Currently only supported through Stripe integration
+- **Custom Domains**: Only supported when deployed on Vercel
+- **AI Features**: All AI functionality is channeled through ManagePrompt and requires their service
+
+## Environment Configuration
+
+Make sure to configure the following in your environment files:
+
+- Database connection details (Supabase)
+- Authentication keys
+- Stripe keys (if using billing features)
+- ManagePrompt API keys (if using AI features)
+- Any other third-party service credentials
+
+For detailed environment variable setup, refer to the `.env.example` files in the respective app directories.
+
diff --git a/apps/page/.env.example b/apps/page/.env.example
new file mode 100644
index 0000000..bc346e6
--- /dev/null
+++ b/apps/page/.env.example
@@ -0,0 +1,9 @@
+NEXT_PUBLIC_SUPABASE_URL=
+NEXT_PUBLIC_SUPABASE_ANON_KEY=
+SUPABASE_SERVICE_ROLE_KEY=
+
+# Inngest
+INNGEST_EVENT_KEY=
+
+# Arcjet
+ARCJET_KEY=
diff --git a/apps/page/.gitignore b/apps/page/.gitignore
index 70683a2..3978add 100644
--- a/apps/page/.gitignore
+++ b/apps/page/.gitignore
@@ -35,8 +35,7 @@ yarn-error.log*
.env*
.flaskenv*
-!.env.project
-!.env.vault
+!.env.example
# IDE
.idea
diff --git a/apps/page/README.md b/apps/page/README.md
index bdfa8c6..5887f1b 100644
--- a/apps/page/README.md
+++ b/apps/page/README.md
@@ -1,19 +1,3 @@
## Pages App
This folder contains the pages app which renders user changelog pages.
-
-### Environment Variables
-
-```
-# Supabase details from https://app.supabase.io
-NEXT_PUBLIC_SUPABASE_URL=
-NEXT_PUBLIC_SUPABASE_ANON_KEY=
-SUPABASE_SERVICE_ROLE_KEY=
-
-# Inngest
-INNGEST_EVENT_KEY=
-INNGEST_SIGNING_KEY=
-
-# Arcjet
-ARCJET_KEY=
-```
diff --git a/apps/page/pages/_sites/[site]/index.tsx b/apps/page/pages/_sites/[site]/index.tsx
index 817d562..2ed083f 100644
--- a/apps/page/pages/_sites/[site]/index.tsx
+++ b/apps/page/pages/_sites/[site]/index.tsx
@@ -166,9 +166,7 @@ export async function getServerSideProps({
};
}
- console.time("fetchRenderData");
const { page, settings } = await fetchRenderData(site);
- console.timeEnd("fetchRenderData");
if (!page || !settings || !(await isSubscriptionActive(page?.user_id))) {
return {
@@ -176,12 +174,10 @@ export async function getServerSideProps({
};
}
- console.time("fetchPosts");
const { posts, postsCount } = await fetchPosts(String(page?.id), {
pinned_post_id: settings?.pinned_post_id,
limit: 10,
});
- console.timeEnd("fetchPosts");
return {
props: {
diff --git a/apps/web/.env.example b/apps/web/.env.example
new file mode 100644
index 0000000..c959e14
--- /dev/null
+++ b/apps/web/.env.example
@@ -0,0 +1,40 @@
+NEXT_PUBLIC_PAGES_DOMAIN=http://localhost:3000
+
+# Supabase details from https://app.supabase.io
+NEXT_PUBLIC_SUPABASE_URL=
+NEXT_PUBLIC_SUPABASE_ANON_KEY=
+SUPABASE_SERVICE_ROLE_KEY=
+
+# Stripe credentials from https://dashboard.stripe.com/apikeys
+NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
+STRIPE_SECRET_KEY=
+STRIPE_WEBHOOK_SECRET=
+STRIPE_PRICE_ID=
+EMAIL_NOTIFICATION_STRIPE_PRICE_ID=
+
+# Custom domains
+VERCEL_AUTH_TOKEN=
+VERCEL_TEAM_ID=
+VERCEL_PAGES_PROJECT_ID=
+
+# Postmark Emails
+POSTMARK_SERVER_KEY=
+POSTMARK_WEBHOOK_KEY=
+
+# Inngest
+INNGEST_EVENT_KEY=
+INNGEST_SIGNING_KEY=
+
+# Arcjet
+ARCJET_KEY=
+
+# CMS
+NEXT_PUBLIC_SANITY_PROJECT_ID=jeixxcw8
+
+# ManagePrompt
+MANAGEPROMPT_SECRET=
+MANAGEPROMPT_CHANGEGPT_WORKFLOW_ID=
+
+# PostHog
+NEXT_PUBLIC_POSTHOG_KEY=
+NEXT_PUBLIC_POSTHOG_HOST=
diff --git a/apps/web/.gitignore b/apps/web/.gitignore
index 1035fdd..f485dfc 100644
--- a/apps/web/.gitignore
+++ b/apps/web/.gitignore
@@ -35,8 +35,7 @@ yarn-error.log*
.env*
.flaskenv*
-!.env.project
-!.env.vault
+!.env.example
# IDE
.idea
diff --git a/apps/web/README.md b/apps/web/README.md
index 24fd2c0..bb14b2e 100644
--- a/apps/web/README.md
+++ b/apps/web/README.md
@@ -1,45 +1,3 @@
## Web App
This folder contains the dashboard app for the project and all marketing pages.
-
-### Environment Variables
-
-```
-NEXT_PUBLIC_PAGES_DOMAIN=http://localhost:3000
-
-# Supabase details from https://app.supabase.io
-NEXT_PUBLIC_SUPABASE_URL=
-NEXT_PUBLIC_SUPABASE_ANON_KEY=
-SUPABASE_SERVICE_ROLE_KEY=
-SUPABASE_WEBHOOK_KEY=
-
-# Stripe credentials from https://dashboard.stripe.com/apikeys
-NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
-STRIPE_SECRET_KEY=
-STRIPE_WEBHOOK_SECRET=
-STRIPE_PRICE_ID=
-EMAIL_NOTIFICATION_STRIPE_PRICE_ID=
-
-# Open AI
-OPENAI_API_KEY=
-
-# Custom domains
-VERCEL_AUTH_TOKEN=
-VERCEL_TEAM_ID=
-VERCEL_PAGES_PROJECT_ID=
-
-# Postmark Emails
-POSTMARK_SERVER_KEY=
-POSTMARK_WEBHOOK_KEY=
-
-# Inngest
-INNGEST_EVENT_KEY=
-INNGEST_SIGNING_KEY=
-
-# CMS
-NEXT_PUBLIC_SANITY_PROJECT_ID=
-
-# ManagePrompt
-MANAGEPROMPT_SECRET=
-MANAGEPROMPT_CHANGEGPT_WORKFLOW_ID=
-```
\ No newline at end of file
diff --git a/apps/web/components/layout/footer.component.tsx b/apps/web/components/layout/footer.component.tsx
index 0346674..21cf9fe 100644
--- a/apps/web/components/layout/footer.component.tsx
+++ b/apps/web/components/layout/footer.component.tsx
@@ -25,6 +25,14 @@ const navigation = {
name: "ChangeCraftAI",
href: "/free-tools/ai-changelog-generator",
},
+ {
+ name: "SemVer Calculator",
+ href: "/free-tools/semantic-version-calculator",
+ },
+ {
+ name: "Release Calendar",
+ href: "/free-tools/release-calendar",
+ },
{ name: "Blog", href: ROUTES.BLOG },
],
legal: [
diff --git a/apps/web/pages/free-tools/release-calendar.tsx b/apps/web/pages/free-tools/release-calendar.tsx
new file mode 100644
index 0000000..91a2a15
--- /dev/null
+++ b/apps/web/pages/free-tools/release-calendar.tsx
@@ -0,0 +1,1123 @@
+import { CalendarIcon, RefreshIcon, ViewGridIcon, ViewListIcon, PresentationChartLineIcon } from "@heroicons/react/solid";
+import classNames from "classnames";
+import { InferGetServerSidePropsType } from "next";
+import Head from "next/head";
+import Link from "next/link";
+import { useCallback, useState, useEffect } from "react";
+import {
+ createToastWrapper,
+ notifyError,
+ notifySuccess,
+} from "../../components/core/toast.component";
+import FooterComponent from "../../components/layout/footer.component";
+import MarketingHeaderComponent from "../../components/marketing/marketing-header.component";
+import usePrefersColorScheme from "../../utils/hooks/usePrefersColorScheme";
+
+interface Release {
+ id: string;
+ version: string;
+ date: string;
+ title: string;
+ type: "major" | "minor" | "patch" | "hotfix";
+ status: "planned" | "in-progress" | "released" | "delayed";
+ description?: string;
+}
+
+type ViewMode = "timeline" | "calendar";
+
+export default function ReleaseCalendar({
+ title,
+ description,
+ keywords,
+ canonicalUrl,
+}: InferGetServerSidePropsTypePress ESC to exit โข Use view toggle to switch between timeline and calendar
+No releases to present
+Exit presentation mode to add releases
++ {new Date(release.date).toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric' + })} +
+ {release.description && ( ++ {release.description} +
+ )} +No releases planned yet
++ Add your first release or load sample data to get started +
+ ++ {new Date(release.date).toLocaleDateString('en-US', { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric' + })} +
+ {release.description && ( ++ {release.description} +
+ )} +
+ {result.version}
+
+ + Enter a version and select change type to calculate the next semantic version +
+
+ {result.original}
+
+ โ
+
+ {result.new}
+
+ + Enter multiple versions (one per line) to calculate their next semantic versions +
+1.0.0 - Basic version2.1.3-alpha.1 - With prerelease1.0.0+build.123 - With build metadata3.0.0-rc.1+build.456 - Full version+ Enter a version string to validate its semantic versioning format +
++ Semantic Versioning (SemVer) is a versioning scheme for software that aims to convey meaning about the underlying changes with each new release. +
++ Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. +
+