diff --git a/.dockerignore b/.dockerignore index 6b107d1d..f838579c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,7 @@ node_modules +.git +.github +.vscode dist build .env diff --git a/.env.example b/.env.example index 518b9201..fbfaca48 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,26 @@ -KENER_SECRET_KEY=please_change_me -NODE_ENV=production -PORT=3000 +TZ=Etc/UTC +KENER_SECRET_KEY=please_change_me_to_something_secure + +# For SQLite database... +DATABASE_URL=sqlite://./database/kener.sqlite.db + +# For PostgreSQL database... +# DATABASE_URL=postgresql://db_user:db_password@localhost:5432/kener_db +# POSTGRES_PASSWORD=some_super_random_secure_password + +# For MySQL database... +# DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/kener_db +# MYSQL_PASSWORD=some_super_random_secure_password + KENER_BASE_PATH="" -RESEND_API_KEY= ORIGIN=http://localhost:3000 -DATABASE_URL=sqlite://./database/kener.sqlite.db -TZ=UTC + +RESEND_API_KEY="" RESEND_SENDER_EMAIL=Accounts -# DATABASE_URL=postgresql://myuser:mypassword@localhost:5432/mydatabase -# DATABASE_URL=mysql://root:password@127.0.0.1:3306/kener \ No newline at end of file +# Likely no need to change... +# NODE_ENV=production # already defined in container +# PORT=3000 # default port Kener service is exposed upon + +# Add the below variable if you would like to ‘white-label’ the product (aka. remove some of the attributions scattered throughout the app) +# WHITE_LABEL=true \ No newline at end of file diff --git a/.github/workflows/protect-readme.yml b/.github/workflows/protect-readme.yml new file mode 100644 index 00000000..e2ece109 --- /dev/null +++ b/.github/workflows/protect-readme.yml @@ -0,0 +1,21 @@ +name: Prevent Direct README Changes + +on: + pull_request: + paths: + - "README.md" + +jobs: + check-readme: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4.2.2 + + - name: Detect direct README changes + run: | + if git diff --name-only origin/main | grep -q "README.md"; then + echo "❌ Direct modifications to README.md are not allowed!" + echo "Please update README.md.template instead." + exit 1 + fi diff --git a/.github/workflows/publish-images.yml b/.github/workflows/publish-images.yml new file mode 100644 index 00000000..a95eebd1 --- /dev/null +++ b/.github/workflows/publish-images.yml @@ -0,0 +1,165 @@ +--- +name: Publish Docker image to Docker Hub and GitHub Container Registry +on: + push: + branches: + - main + tags: + - "*.*.*" + paths-ignore: + - '**/*.md' + - 'docs/**' + workflow_dispatch: # Allows manual execution + +env: + ALPINE_VERSION: "23-alpine" + DEBIAN_VERSION: "23-slim" + # Registry URLs + DOCKERHUB_REGISTRY: docker.io + GITHUB_REGISTRY: ghcr.io + # Docker Hub image name (using Docker Hub username) + DOCKERHUB_IMAGE_NAME: ${{ secrets.DOCKER_USERNAME }}/${{ github.event.repository.name }} + # GitHub image name (formatted as `account/repo`) + GITHUB_IMAGE_NAME: ${{ github.repository }} + +jobs: + build_and_push_to_registries: + name: Push Docker images to Docker Hub and GitHub Container Registry + strategy: + matrix: + variant: [alpine, debian] + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Check out the repo + uses: actions/checkout@v4.2.2 + + # Install the cosign tool (except on PR) + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@v3.8.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.8.0 + + # Log in to Docker Hub (except on PR) + - name: Log in to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3.3.0 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + # Log in to GitHub Container Registry (except on PR) + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3.3.0 + with: + registry: ${{ env.GITHUB_REGISTRY }} + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Combined metadata extraction for both registries + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5.6.1 + with: + images: | + ${{ env.DOCKERHUB_IMAGE_NAME }} + ${{ env.GITHUB_REGISTRY }}/${{ env.GITHUB_IMAGE_NAME }} + tags: | + # Tag branches as type=ref,event=branch + # For Alpine variant, add suffix + type=ref,event=branch,suffix=-${{ matrix.variant }},enable=${{ matrix.variant == 'alpine' }} + type=ref,event=branch,enable=${{ matrix.variant == 'debian' }} + # Tag matrix.variant (Alpine) releases as version (1.0.0), major.minor (1.0), major (1), and matrix.variant (alpine) + type=semver,pattern={{version}},suffix=-${{ matrix.variant }},enable=${{ matrix.variant == 'alpine' }} + type=semver,pattern={{major}}.{{minor}},suffix=-${{ matrix.variant }},enable=${{ matrix.variant == 'alpine' }} + type=semver,pattern={{major}},suffix=-${{ matrix.variant }},enable=${{ matrix.variant == 'alpine' }} + type=raw,value=${{ matrix.variant }},enable=${{ matrix.variant == 'alpine' && github.ref == 'refs/heads/main' }} + # Tag default (Debian) releases as version (1.0.0), major.minor (1.0), major (1), and latest + type=semver,pattern={{version}},enable=${{ matrix.variant == 'debian' }} + type=semver,pattern={{major}}.{{minor}},enable=${{ matrix.variant == 'debian' }} + type=semver,pattern={{major}},enable=${{ matrix.variant == 'debian' }} + type=raw,value=latest,enable=${{ matrix.variant == 'debian' && github.ref == 'refs/heads/main' }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3.3.0 + + # Build and push Docker image with Buildx to both registries (don't push on PR) + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6.13.0 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + VARIANT=${{ matrix.variant }} + ALPINE_VERSION=${{ env.ALPINE_VERSION }} + DEBIAN_VERSION=${{ env.DEBIAN_VERSION }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digests + - name: Sign the published Docker images + if: ${{ github.event_name != 'pull_request' }} + env: + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + run: | + echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} + + generate_readme: + needs: build_and_push_to_registries # Runs only after build_and_push_to_registries completes successfully + name: Generate README from template + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4.2.2 + + - name: Extract Release Versions + id: meta + uses: docker/metadata-action@v5.6.1 + with: + images: rajnandan1/kener + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Setup Node.js + uses: actions/setup-node@v4.2.0 + with: + node-version: "20" + + - name: Install Dependencies + run: npm install mustache dotenv + + - name: Generate README.md + env: + KENER_BUILD_FULL_VERSION: ${{ steps.meta.outputs.tags[0] }} # e.g., 1.2.3 + KENER_BUILD_MAJOR_MINOR_VERSION: ${{ steps.meta.outputs.tags[1] }} # e.g., 1.2 + KENER_BUILD_MAJOR_VERSION: ${{ steps.meta.outputs.tags[2] }} # e.g., 1 + run: node scripts/generate-readme.js + + - name: Commit and Push Changes + run: | + git config --global user.name 'github-actions' + git config --global user.email 'github-actions@github.com' + git add README.md + git commit -m "Auto-generate README.md with release versions" || echo "No changes to commit" + git push diff --git a/.github/workflows/publishImage.yml b/.github/workflows/publishImage.yml deleted file mode 100644 index 246d5304..00000000 --- a/.github/workflows/publishImage.yml +++ /dev/null @@ -1,61 +0,0 @@ ---- -name: Publish Docker image to Dockerhub and GHCR -on: - push: - branches: - - main - tags: - - "*.*.*" - paths-ignore: - - '**/*.md' - - 'docs/**' -jobs: - push_to_registry: - name: Push Docker image to Docker Hub - runs-on: ubuntu-latest - if: ${{ vars.DOCKERHUB_IMAGE_NAME != '' || vars.GHCR_IMAGE_NAME != '' }} - permissions: - packages: write - contents: read - steps: - - name: Check out the repo - uses: actions/checkout@v2 - - name: Log in to Docker Hub - if: ${{ github.event_name != 'pull_request' && vars.DOCKERHUB_IMAGE_NAME != '' - }} - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - name: Login to GitHub Container Registry - if: ${{ github.event_name != 'pull_request' && vars.GHCR_IMAGE_NAME != '' }} - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v3 - with: - images: | - ${{ vars.DOCKERHUB_IMAGE_NAME }} - ${{ vars.GHCR_IMAGE_NAME }} - tags: | - type=raw,value=latest,enable=${{ endsWith(github.ref, 'main') }} - type=ref,event=branch,enable=${{ !endsWith(github.ref, 'main') }} - type=semver,pattern={{version}} - flavor: | - latest=false - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Build and push Docker image - uses: docker/build-push-action@v4 - with: - context: . - push: ${{ github.event_name != 'pull_request' && !env.ACT}} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 diff --git a/Dockerfile b/Dockerfile index 78229022..5ed9872d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,52 +1,146 @@ -FROM node:23 +# syntax=docker/dockerfile:1 -# Install necessary packages including tzdata for timezone setting +# Global build arguments +ARG ALPINE_VERSION=23.7.0-alpine3.21 +ARG DEBIAN_VERSION=23.7.0-bookworm-slim +ARG VARIANT=debian + +#==========================================================# +# STAGE 1: BUILD STAGE # +#==========================================================# + +FROM node:${DEBIAN_VERSION} AS builder-debian RUN apt-get update && apt-get install -y \ - build-essential \ - libnode108 \ - nodejs \ - python3 \ - sqlite3 \ - libsqlite3-dev \ - make \ - node-gyp \ - g++ \ - tzdata \ - iputils-ping && \ + build-essential=12.9 \ + python3=3.11.2-1+b1 \ + sqlite3=3.40.1-2+deb12u1 \ + libsqlite3-dev=3.40.1-2+deb12u1 \ + make=4.3-4.1 \ + node-gyp=9.3.0-2 \ + g++=4:12.2.0-3 \ + tzdata=2024b-0+deb12u1 \ + iputils-ping=3:20221126-1+deb12u1 && \ rm -rf /var/lib/apt/lists/* -# Set the timezone environment variable and the application environment -ARG KENER_BASE_PATH= -ENV TZ=Etc/UTC -ENV KENER_BASE_PATH=${KENER_BASE_PATH} +FROM node:${ALPINE_VERSION} AS builder-alpine +RUN apk add --no-cache --update \ + build-base=0.5-r3 \ + python3=3.12.9-r0 \ + py3-pip=24.3.1-r0 \ + make=4.4.1-r2 \ + g++=14.2.0-r4 \ + sqlite=3.48.0-r0 \ + sqlite-dev=3.48.0-r0 \ + tzdata=2024b-r1 \ + iputils=20240905-r0 + +FROM builder-${VARIANT} AS builder + +# Set environment variables +ENV NPM_CONFIG_LOGLEVEL=error \ + VITE_BUILD_ENV=production # Set the working directory WORKDIR /app -# Copy package files and install dependencies +# Copy package files for dependency installation COPY package*.json ./ -RUN npm install && npm cache clean --force -# Copy the rest of the application code +# Install all dependencies, including `devDependencies` (cache enabled for faster builds) +RUN --mount=type=cache,target=/root/.npm \ + npm ci && \ + npm cache clean --force + +# Copy application source code COPY . . -# remove dir src/routes/(docs) -RUN rm -rf src/routes/\(docs\) +# TODO: Reevaluate permissions (possibly reduce?)... +# Remove docs directory and ensure required directories exist +RUN rm -rf src/routes/\(docs\) && \ + mkdir -p uploads database && \ + # TODO: Consider changing below to `chmod -R u-rwX,g=rX,o= uploads database` + chmod -R 750 uploads database + +# Build the application and remove `devDependencies` +RUN npm run build && \ + npm prune --omit=dev + +#==========================================================# +# STAGE 2: PRODUCTION/FINAL STAGE # +#==========================================================# + +FROM node:${DEBIAN_VERSION} AS final-debian +# TODO: Confirm with @rajnandan1 which of these packages are necessary for the Debian (default), final stage +RUN apt-get update && apt-get install --no-install-recommends -y \ + iputils-ping=3:20221126-1+deb12u1 \ + sqlite3=3.40.1-2+deb12u1 \ + tzdata=2024b-0+deb12u1 \ + wget=1.21.3-1+b1 && \ + rm -rf /var/lib/apt/lists/* + +FROM node:${ALPINE_VERSION} AS final-alpine +# TODO: Confirm with @rajnandan1 which of these packages are necessary for the Alpine Linux, final stage +RUN apk add --no-cache --update \ + iputils=20240905-r0 \ + sqlite=3.48.0-r0 \ + tzdata=2024b-r1 + +FROM final-${VARIANT} AS final + +ARG PORT=3000 \ + USERNAME=node -# Ensure /app/uploads and /app/database have rw permissions -RUN mkdir -p /app/uploads /app/database && \ - chmod -R 777 /app/uploads /app/database +# Set environment variables +ENV HEALTHCHECK_PORT=$PORT \ + HEALTHCHECK_PATH= \ + NODE_ENV=production \ + NPM_CONFIG_LOGLEVEL=error \ + PORT=$PORT \ + TZ=Etc/UTC -# Build the application -RUN npm run build +# Set the working directory +WORKDIR /app -# Argument for the port -ARG PORT=3000 -# Set the environment variable for the port -ENV PORT=$PORT +# TODO: Confirm with @rajnandan1 which files/directories are absolutely necessary for production build +# Copy package files build artifacts, and necessary files from builder stage +COPY --chown=node:node --from=builder /app/src/lib/ ./src/lib/ +COPY --chown=node:node --from=builder /app/build ./build +COPY --chown=node:node --from=builder /app/uploads ./uploads +COPY --chown=node:node --from=builder /app/database ./database +# TODO: Consider changing from copying `node_modules` to instead letting `npm ci --omit=dev` handle production dependencies. Right now, copying `node_modules` is leading to a smaller image, whereas letting `npm ci` handle the install in final image is slightly faster, but leads to larger image size. IMO, having a slightly longer build time (e.g. ~10 sec.) is better in the end to have a smaller image. +COPY --chown=node:node --from=builder /app/node_modules ./node_modules +COPY --chown=node:node --from=builder /app/migrations ./migrations +COPY --chown=node:node --from=builder /app/seeds ./seeds +COPY --chown=node:node --from=builder /app/static ./static +COPY --chown=node:node --from=builder /app/entrypoint.sh ./entrypoint.sh +COPY --chown=node:node --from=builder /app/knexfile.js ./knexfile.js +COPY --chown=node:node --from=builder /app/main.js ./main.js +COPY --chown=node:node --from=builder /app/openapi.json ./openapi.json +COPY --chown=node:node --from=builder /app/openapi.yaml ./openapi.yaml + +# Ensure necessary directories are writable +VOLUME ["/uploads", "/database"] + +# Set container timezone and make entrypoint script executable +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \ + chmod +x ./entrypoint.sh + # TODO: To improve security, consider dropping unnecessary capabilities instead of granting image all network capabilities of host. (Maybe `setcap cap_net_raw+p /usr/bin/ping`, etc.) Could also drop all and then grant only the capabilities that are explicitly needed. Some examples are commented out below... + # setcap cap_net_bind_service=+ep /usr/local/bin/node + # setcap cap_net_bind_service=+ep /usr/bin/ping + # setcap cap_net_bind_service=+ep /usr/bin/ping6 + # setcap cap_net_bind_service=+ep /usr/bin/tracepath + # setcap cap_net_bind_service=+ep /usr/bin/clockdiff # Expose the application port EXPOSE $PORT -# Set the command to run the application +# TODO: Consider switching to lighter-weight `nc` (Netcat) command-line utility (would remove `wget` in Debian build, however, it's already pretty small, so probably doesn't matter as `wget` is more powerful) +HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ + CMD wget --quiet --spider http://localhost:$HEALTHCHECK_PORT$HEALTHCHECK_PATH || exit 1 + +# TODO: Revisit letting user define $PUID & $PGID overrides (e.g. `addgroup -g $PGID newgroup && adduser -D -G newgroup -u $PUID node`) as well as potentially ensure no root user exists. (Make sure no processes are running as root, first!) +# Use a non-root user (recommended for security) +USER $USERNAME + +ENTRYPOINT ["/app/entrypoint.sh"] CMD ["node", "main"] \ No newline at end of file diff --git a/README.md b/README.md index 36978cd6..9db7101f 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,13 @@

GitHub Repo stars Awesome status page - Docker Kener - Awesome self hosted + Awesome self hosted +

+ +

+ Docker Kener + Docker Image Size + Docker Image Size

@@ -43,13 +48,19 @@ ## What is Kener? -Kener is status page system built with Sveltekit and NodeJS. It does not try to replace the Datadogs and Atlassian of the world. It tries to help some who wants to come up with a status page that looks nice and minimum overhead, in a modern way. +**Kener** is a sleek and lightweight status page system built with **SvelteKit** and **NodeJS**. It’s not here to replace heavyweights like Datadog or Atlassian but rather to offer a simple, modern, and hassle-free way to set up a great-looking status page with minimal effort. + +Designed with **ease of use** and **customization in mind**, Kener provides all the essential features you’d expect from a status page—without unnecessary complexity. + +### Why Kener? -It is carefully crafted to be easy to use and customize. +✅  Minimal overhead – Set up quickly with a clean, modern UI
+✅  Customizable – Easily tailor it to match your brand
+✅  Open-source & free – Because great tools should be accessible to everyone -It comes with all the basic asks for a status page. It is open-source and free to use. +### What's in a Name? -Kener name is derived from the word "Kene" which means "how is it going" in Assamese, then .ing because it was a cheaply available domain. +“Kener” is inspired by the Assamese word _“Kene”_, meaning _“how’s it going?”_. The _‘.ing’_ was added because, well… that domain was available. 😄 ## Installation @@ -66,8 +77,74 @@ npm run dev ### Docker -The latest image is available on DockerHub at [`rajnandan1/kener:latest`](https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=latest). -Download and use the sample [docker-compose.yml](https://github.com/rajnandan1/kener/blob/main/docker-compose.yml). +Official Docker images for **Kener** are available on [Docker Hub](https://hub.docker.com/r/rajnandan1/kener). Multiple versions are maintained to support different use cases. + +![Docker Image Version (latest semver)](https://img.shields.io/docker/v/rajnandan1/kener?sort=semver&label=Latest%20Stable%20Release) + + + + +#### Usage + +Pull the latest stable version: + +```sh +docker pull rajnandan1/kener:latest +``` + +Or use the smaller, Alpine-based variant: + +```sh +docker pull rajnandan1/kener:alpine +``` + +For a production setup, refer to the sample [docker-compose.yml](https://github.com/rajnandan1/kener/blob/main/docker-compose.yml). +This keeps things clean, structured, and easy to read while preserving all the details. ### One Click @@ -77,67 +154,64 @@ Download and use the sample [docker-compose.yml](https://github.com/rajnandan1/k Here are some of the features that you get out of the box. Please read the documentation to know how to use them. -### Monitoring and Tracking - -- Advanced application performance monitoring tools -- Real-time network monitor software capabilities -- Polls HTTP endpoint or Push data to monitor using Rest APIs -- Adjusts Timezones for visitors -- Categorize Monitors into different Sections -- Cron-based scheduling for monitors. Minimum per minute -- Construct complex API Polls - Chain, Secrets etc -- Supports a Default Status for Monitors -- Supports base path for hosting in k8s -- Pre-built docker image for easy deployment -- Automatically adjusts timezones for visitors - -### Customization and Branding - -- Customizable status page -- Badge generation for status and uptime of Monitors -- Support for custom domains -- Embed Monitor as an iframe or widget -- Light + Dark Theme -- Internationalization support -- Beautifully Crafted Status Page - -### Incident Management - -- Incident Management -- Incident Communication -- Comprehensive APIs for Incident Management - -### User Experience and Design - -- Good Accessibility Score -- Easy installation and setup -- User-friendly interface -- Responsive design for various devices -- Auto SEO and Social Media ready -- Server Side Rendering +### 📊  Monitoring and Tracking + +- Advanced **application performance monitoring** tools +- **Real-time network monitoring** capabilities +- Supports **polling HTTP endpoints** or **pushing data** via REST APIs +- **Timezone auto-adjustment** for visitors +- Organize monitors into **custom sections** +- **Cron-based scheduling** (minimum: **every minute**) +- **Create complex API polls** (chaining, secrets, etc.) +- Set a **default status** for monitors +- Supports **base path hosting in Kubernetes (k8s)** +- **Pre-built Docker images** for easy deployment + +### 🎨  Customization and Branding + +- Fully **customizable status page** +- **Badge generation** for status and uptime tracking +- Support for **custom domains** +- Embed monitors as **iframes or widgets** +- **Light & Dark Mode** +- **Internationalization (i18n) support** +- **Sleek, beautifully crafted UI** + +### 🚨  Incident Management + +- **Incident tracking & communication** tools +- **Comprehensive APIs** for incident management + +### 🧑‍💻  User Experience and Design + +- **Accessible & user-friendly interface** +- **Quick & easy installation** +- **Responsive design** for all devices +- **Auto SEO & Social Media ready** +- **Server-Side Rendering (SSR) for better performance**

Visitor Stats
-## Technologies used +## Technologies Used - [SvelteKit](https://kit.svelte.dev/) - [shadcn-svelte](https://www.shadcn-svelte.com/) ## Support Me -If you are using Kener and want to support me, you can do so by sponsoring me on GitHub or buying me a coffee. +If you’re enjoying Kener and want to support its development, consider sponsoring me on GitHub or treating me to a coffee. Your support helps keep the project growing! 🚀 [Sponsor Me Using Github](https://github.com/sponsors/rajnandan1) -[Buy Me a Coffee](https://www.buymeacoffee.com/rajnandan1) +☕  [Buy Me a Coffee](https://www.buymeacoffee.com/rajnandan1) ![image](https://badges.pufler.dev/visits/rajnandan1/kener) ## Contributing -If you want to contribute to Kener, please read the [Contributing Guide](https://github.com/rajnandan1/kener/blob/main/.github/CONTRIBUTING.md). +If you want to contribute to Kener, please read the [Contribution Guide](https://github.com/rajnandan1/kener/blob/main/.github/CONTRIBUTING.md). ## Star History diff --git a/README.template.md b/README.template.md new file mode 100644 index 00000000..0737e189 --- /dev/null +++ b/README.template.md @@ -0,0 +1,217 @@ +# Kener - Stunning Status Pages + +

+ kener example illustration +

+ +

+ GitHub Repo stars + Awesome status page + Awesome self hosted +

+ +

+ Docker Kener + Docker Image Size + Docker Image Size +

+ +

+ GitHub Workflow Status + + GitHub issues +

+ +

+ + Kener on Product Hunt + +

+ +

+ + + 🔔 + + + + 🚀 + + + + 🚧 + +

+ +| [🌍 Live Server](https://kener.ing) | [🎉 Quick Start](https://kener.ing/docs/quick-start) | [🗄 Documentation](https://kener.ing/docs/home) | +| ----------------------------------- | ---------------------------------------------------- | ----------------------------------------------- | + +## What is Kener? + +**Kener** is a sleek and lightweight status page system built with **SvelteKit** and **NodeJS**. It’s not here to replace heavyweights like Datadog or Atlassian but rather to offer a simple, modern, and hassle-free way to set up a great-looking status page with minimal effort. + +Designed with **ease of use** and **customization in mind**, Kener provides all the essential features you’d expect from a status page—without unnecessary complexity. + +### Why Kener? + +✅  Minimal overhead – Set up quickly with a clean, modern UI
+✅  Customizable – Easily tailor it to match your brand
+✅  Open-source & free – Because great tools should be accessible to everyone + +### What's in a Name? + +“Kener” is inspired by the Assamese word _“Kene”_, meaning _“how’s it going?”_. The _‘.ing’_ was added because, well… that domain was available. 😄 + +## Installation + +### Manual + +```shell +# Clone the repository +git clone https://github.com/rajnandan1/kener.git +cd kener +npm install +cp .env.example .env +npm run dev +``` + +### Docker + +Official Docker images for **Kener** are available on [Docker Hub](https://hub.docker.com/r/rajnandan1/kener). Multiple versions are maintained to support different use cases. + +![Docker Image Version (latest semver)](https://img.shields.io/docker/v/rajnandan1/kener?sort=semver&label=Latest%20Stable%20Release) + +#### Available Tags + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Image TagDescription
Debian 12 (Bookwork Slim) w/ Node.js v23.7.0  (default)
latestLatest stable release (aka {{kener_full_version}})
{{kener_full_version}}Specific release version
{{kener_major_minor_version}}Major-minor version tag pointing to the latest patch ({{kener_full_version}}) release within that minor version ({{kener_major_minor_version}}.x)
{{kener_major_version}}Major version tag pointing to the latest stable ({{kener_full_version}}) release within that major version ({{kener_major_version}}.x.x)
Alpine Linux 3.21 w/ Node.js v23.7.0  (smallest image size)
alpineLatest stable release (aka {{kener_full_version}})
{{kener_full_version}}-alpineSpecific release version
{{kener_major_minor_version}}-alpineMajor-minor version tag pointing to the latest patch ({{kener_full_version}}) release within that minor version ({{kener_major_minor_version}}.x)
{{kener_major_version}}-alpineMajor version tag pointing to the latest stable ({{kener_full_version}}) release within that major version ({{kener_major_version}}.x.x)
+ +#### Usage + +Pull the latest stable version: + +```sh +docker pull rajnandan1/kener:latest +``` + +Or use the smaller, Alpine-based variant: + +```sh +docker pull rajnandan1/kener:alpine +``` + +For a production setup, refer to the sample [docker-compose.yml](https://github.com/rajnandan1/kener/blob/main/docker-compose.yml). +This keeps things clean, structured, and easy to read while preserving all the details. + +### One Click + +[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/template/spSvic?referralCode=1Pn7vs) + +## Features + +Here are some of the features that you get out of the box. Please read the documentation to know how to use them. + +### 📊  Monitoring and Tracking + +- Advanced **application performance monitoring** tools +- **Real-time network monitoring** capabilities +- Supports **polling HTTP endpoints** or **pushing data** via REST APIs +- **Timezone auto-adjustment** for visitors +- Organize monitors into **custom sections** +- **Cron-based scheduling** (minimum: **every minute**) +- **Create complex API polls** (chaining, secrets, etc.) +- Set a **default status** for monitors +- Supports **base path hosting in Kubernetes (k8s)** +- **Pre-built Docker images** for easy deployment + +### 🎨  Customization and Branding + +- Fully **customizable status page** +- **Badge generation** for status and uptime tracking +- Support for **custom domains** +- Embed monitors as **iframes or widgets** +- **Light & Dark Mode** +- **Internationalization (i18n) support** +- **Sleek, beautifully crafted UI** + +### 🚨  Incident Management + +- **Incident tracking & communication** tools +- **Comprehensive APIs** for incident management + +### 🧑‍💻  User Experience and Design + +- **Accessible & user-friendly interface** +- **Quick & easy installation** +- **Responsive design** for all devices +- **Auto SEO & Social Media ready** +- **Server-Side Rendering (SSR) for better performance** + +
+ Visitor Stats +
+ +## Technologies Used + +- [SvelteKit](https://kit.svelte.dev/) +- [shadcn-svelte](https://www.shadcn-svelte.com/) + +## Support Me + +If you’re enjoying Kener and want to support its development, consider sponsoring me on GitHub or treating me to a coffee. Your support helps keep the project growing! 🚀 + +[Sponsor Me Using Github](https://github.com/sponsors/rajnandan1) + +☕  [Buy Me a Coffee](https://www.buymeacoffee.com/rajnandan1) + +![image](https://badges.pufler.dev/visits/rajnandan1/kener) + +## Contributing + +If you want to contribute to Kener, please read the [Contribution Guide](https://github.com/rajnandan1/kener/blob/main/.github/CONTRIBUTING.md). + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=rajnandan1/kener&type=Date)](https://star-history.com/#rajnandan1/kener&Date) diff --git a/docker-compose.yml b/docker-compose.yml index 12b029b4..039eddff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,26 +1,61 @@ +# Docker Compose Configuration +# Description: This file sets up a multi-container environment for Kener (https://github.com/rajnandan1/kener). +# Last Updated: 2025-02-08 +# Docker Compose Version: 3.8 +# Notes: Ensure that you specify a random value for the `KENER_SECRET_KEY` environment variable before running `docker-compose up -d`. + version: '3.8' + services: kener: - image: rajnandan1/kener:latest + image: rajnandan1/kener:latest # Change to 'rajnandan1/kener:alpine' for an even smaller image! 😁🚀 container_name: kener - #env_file: .env #uncomment this, if you are using .env file + # env_file: custom.env # Uncomment this if you are needing to export environment variables from a custom environment file. By default, Docker will import any variables that exist in `.env` environment: - - TZ=Etc/UTC - - NODE_ENV=production - #- PORT=3000 - #- KENER_BASE_PATH= - #- RESEND_API_KEY= - #- ORIGIN= - #- KENER_SECRET_KEY= - #- RESEND_SENDER_EMAIL= - #- DATABASE_URL= + TZ: Etc/UTC + KENER_SECRET_KEY: replace_me_with_a_random_string # Keep private!! - best to define in `.env` file or through Docker Secret + # DATABASE_URL: custom_db_url # By default, a SQLite database is used - you may override the database url/type here + # RESEND_API_KEY: + # RESEND_SENDER_EMAIL: + ### You most likely will NOT need to change anything below this line. Be sure you know what you're doing!! (https://kener.ing/docs/deployment/#docker-environment-variables) - ### Most likely DO NOT need to change anything below this ### - - #- PORT=3000 Port app listens on IN CONTAINER + # PORT: 3000 # Port that app listens on in the container + # KENER_BASE_PATH: # By default, Kener runs at `/`. You may change this to be, e.g. `/status`, etc. Do NOT add a trailing slash!! (more info here: https://kener.ing/docs/deployment/#docker-environment-variables) + # ORIGIN: http://localhost:3000 + # NODE_ENV: production # This is already set to "production" by default within the container ports: - '3000:3000/tcp' volumes: - - '.:/app/database:rw' - - '.:/app/uploads:rw' + - data:/app/database # We suggest using a Docker named volume, which is more performant for databases + - $(pwd)/uploads:/app/uploads + # read_only: true # Uncommenting this fortifies security by marking the container's filesystem as read-only (aka no data can be written to the container's filesystem except for explicitly defined writable volumes and bind mounts, an exception has already been defined for `/database` and `/uploads`) + restart: unless-stopped + # depends_on: # <-- Uncomment if you would like to use PostgreSQL or MySQL + # - postgres # ...instead of SQLite + # - mysql # + + # Only use below section if you would like to utilize PostgreSQL instead of Kener's default SQLite database. (Don't forget to set `DATABASE_URL` in `kener` service to be: `DATABASE_URL=postgresql://db_user:db_password@localhost:5432/kener_db`) + postgres: + image: postgres:alpine + name: kener_db + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: some_super_random_secure_password # Best to define this in `.env` or via Docker Secret!! + POSTGRES_DB: kener_db + restart: unless-stopped + + # Only use below section if you would like to utilize MySQL instead of Kener's default SQLite database. (Don't forget to set `DATABASE_URL` in `kener` service to be: `DATABASE_URL=mysql://db_user:db_password@localhost:3306/kener_db`) + mysql: + image: mariadb:11 + name: kener_db + environment: + MYSQL_USER: user + MYSQL_PASSWORD: some_super_random_secure_password # Best to define this in `.env` or via Docker Secret!! + MYSQL_DATABASE: kener_db + MYSQL_RANDOM_ROOT_PASSWORD: true + restart: unless-stopped + +volumes: + data: + name: kener_db \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 00000000..432e624f --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Automatically set PUBLIC_WHITE_LABEL based on WHITE_LABEL +export PUBLIC_WHITE_LABEL="${WHITE_LABEL}" + +# Replace shell with the given command (from CMD or runtime args) +exec "$@" \ No newline at end of file diff --git a/package.json b/package.json index 17c3edad..b2fcd10a 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "devschedule": "node src/lib/server/startup.js", "schedule": "node src/lib/server/startup.js", "development": "vite dev", + "generate-readme": "node scripts/generate-readme.js", "dev": "npm-run-all --parallel devschedule development", "prettify": "prettier --write .", "start": "node main.js" @@ -51,6 +52,7 @@ "concurrently": "^8.2.2", "cross-env": "^7.0.3", "date-picker-svelte": "^2.15.1", + "mustache": "^4.2.0", "postcss": "^8.4.24", "postcss-load-config": "^4.0.1", "prettier": "^3.2.5", diff --git a/scripts/generate-readme.js b/scripts/generate-readme.js new file mode 100644 index 00000000..af1c1bec --- /dev/null +++ b/scripts/generate-readme.js @@ -0,0 +1,31 @@ +import fs from "fs"; +import Mustache from "mustache"; +import dotenv from "dotenv"; +import { fileURLToPath } from "url"; +import path from "path"; + +// Load environment variables from .env file +dotenv.config(); + +// Resolve paths correctly in ES modules +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Load the template +const templatePath = path.resolve(__dirname, "../README.template.md"); +const template = fs.readFileSync(templatePath, "utf-8"); + +// Load environment variables and provide default values +const data = { + kener_full_version: process.env.KENER_BUILD_FULL_VERSION || "N/A", + kener_major_minor_version: process.env.KENER_BUILD_MAJOR_MINOR_VERSION || "N/A", + kener_major_version: process.env.KENER_BUILD_MAJOR_VERSION || "N/A", +}; + +// Render README.md +const output = Mustache.render(template, data); + +// Write to README.md +fs.writeFileSync(path.resolve(__dirname, "../README.md"), output); + +console.log("✅ README.md generated successfully!");