Compare commits

...

9 Commits

Author SHA1 Message Date
ada7140124 change order of env variable
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 8m48s
2025-12-06 22:06:06 -05:00
4d6a23fbf1 test graphql uri fix
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 6m1s
2025-12-06 21:58:54 -05:00
8bda185848 fix env variable
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 4m52s
2025-12-06 21:36:45 -05:00
f035c2e746 test test
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 7m50s
2025-12-06 21:23:50 -05:00
1edc0fec18 test
Some checks are pending
Main / build-and-push-docker-image (20.x) (push) Waiting to run
2025-12-06 21:23:22 -05:00
add1ae525a set args at build time
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 7m9s
2025-12-06 21:08:43 -05:00
9557833df6 added build args and variables
Some checks are pending
Main / build-and-push-docker-image (20.x) (push) Has started running
2025-12-06 20:52:00 -05:00
8ae9946ef9 secure api
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 8m44s
2025-12-06 20:29:16 -05:00
e0974d1468 Fixed production issues, fixed render issues, adde
All checks were successful
Main / build-and-push-docker-image (20.x) (push) Successful in 8m4s
d secret and env variables into dockerfile and gihub actions files, remade dockerfile and github actions

added build stage

fix build step

remove the second section

asdas

test new file

fix env variables

env

enable corepack

test yarn install

new base

remove corepack

fix yarn install

yarn install

new base (again)

new base

corepack

install yarn

enable corepack

revert base

fix yarn

yarn install

fix build errors

new file

remove in-line comment

update copy command

copy node_monudes

test

workdir top level

ls app folder

test ls

update yarn install

copy more files

remove copy

fix copy

move workdir to top level

fix copy

check folder

test

fix copy - last try

prisma gen

database url secret / env variable for ci/cd

args

env var

args

fix env

fix copy

fix build file name

fix start command

added copy

ANOTHER COPY

copy prisma generated files, fix start command

remove standalone config

fix urql url

update display
2025-12-06 18:41:07 -05:00
10 changed files with 293 additions and 96 deletions

View File

@@ -53,6 +53,10 @@ jobs:
context: .
push: ${{ !github.event.pull_request.head.repo.fork }}
tags: ${{ steps.meta.outputs.tags }}
build-args: |
DATABASE_URL=${{ secrets.DATABASE_URL }}
NEXT_PUBLIC_API_TOKEN=${{ secrets.NEXT_PUBLIC_API_TOKEN }}
NEXT_PUBLIC_GRAPHQL_URL=${{ secrets.NEXT_PUBLIC_GRAPHQL_URL }}
- name: Build and Push Latest Docker Image
id: build-and-push-latest
uses: docker/build-push-action@v4
@@ -61,3 +65,7 @@ jobs:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.OWNER }}/${{ env.IMAGE_NAME }}:latest
build-args: |
DATABASE_URL=${{ secrets.DATABASE_URL }}
NEXT_PUBLIC_API_TOKEN=${{ secrets.NEXT_PUBLIC_API_TOKEN }}
NEXT_PUBLIC_GRAPHQL_URL=${{ secrets.NEXT_PUBLIC_GRAPHQL_URL }}

View File

@@ -1,72 +1,49 @@
# syntax=docker.io/docker/dockerfile:1
FROM node:20-alpine AS base
# Enable Corepack
# --- Stage 1: Dependencies ---
FROM node:20-alpine AS dependencies
RUN corepack enable
# Set Yarn to the latest stable version
RUN yarn set version stable
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
RUN corepack prepare yarn@stable --activate
WORKDIR /app
COPY package.json yarn.lock .yarnrc.yml ./
RUN yarn install
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
# --- Stage 2: Builder ---
FROM node:20-alpine AS builder
RUN corepack enable
RUN corepack prepare yarn@stable --activate
ARG DATABASE_URL
ENV DATABASE_URL=${DATABASE_URL}
ENV DATABASE_URL=${DATABASE_URL}
ARG NEXT_PUBLIC_GRAPHQL_URL
ENV NEXT_PUBLIC_GRAPHQL_URL=${NEXT_PUBLIC_GRAPHQL_URL}
ARG NEXT_PUBLIC_API_TOKEN
ENV NEXT_PUBLIC_API_TOKEN=${NEXT_PUBLIC_API_TOKEN}
RUN corepack enable
RUN corepack prepare yarn@stable --activate
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
COPY --from=dependencies /app/node_modules ./node_modules
COPY . ./
RUN yarn prisma-gen
RUN yarn build
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
# --- Stage 3: Runner ---
FROM node:20-alpine AS runner
RUN corepack enable
RUN corepack prepare yarn@stable --activate
ARG DATABASE_URL
ENV DATABASE_URL=${DATABASE_URL}
ARG NEXT_PUBLIC_GRAPHQL_URL
ENV NEXT_PUBLIC_GRAPHQL_URL=${NEXT_PUBLIC_GRAPHQL_URL}
ARG NEXT_PUBLIC_API_TOKEN
ENV NEXT_PUBLIC_API_TOKEN=${NEXT_PUBLIC_API_TOKEN}
RUN corepack enable
RUN corepack prepare yarn@stable --activate
WORKDIR /app
ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=builder /app/src/prisma/generated ./src/prisma/generated
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/.yarn ./.yarn
COPY . ./
EXPOSE 3000
ENV PORT=3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
CMD ["yarn", "start"]

View File

@@ -3,8 +3,7 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
optimizePackageImports: ["@chakra-ui/react"]
},
output: "standalone"
}
};
export default nextConfig;

View File

@@ -15,6 +15,7 @@
"@chakra-ui/charts": "^3.30.0",
"@chakra-ui/react": "^3.30.0",
"@emotion/react": "^11.14.0",
"@escape.tech/graphql-armor": "^3.1.7",
"@prisma/client": "^6.19.0",
"@prisma/extension-accelerate": "^2.0.2",
"@urql/next": "^2.0.0",

View File

@@ -33,7 +33,7 @@ const StatsList = ({
</Text>
</VStack>
<Flex w="80%" flexDirection={{ base: "column", md: "row" }} gap={4}>
{groups ? (
{groups && groups >= 0 ? (
<SingleStatComponent
loading={loading}
title="Groups Bot Helped"

View File

@@ -1,11 +1,54 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { createYoga, Plugin, createSchema } from "graphql-yoga";
import { EnvelopArmorPlugin } from "@escape.tech/graphql-armor";
import resolvers from "@/graphql/resolvers";
import typeDefs from "@/graphql/types";
import { createSchema, createYoga } from "graphql-yoga";
interface NextContext {
params: Promise<Record<string, string>>;
}
const environment = process.env.NODE_ENV || "development";
const isValidApiKey = (apiKey: string): boolean => {
const envApiKey =
process.env.API_TOKEN || process.env.NEXT_PUBLIC_API_TOKEN || "";
return apiKey === envApiKey;
};
function useApiKey(): Plugin {
return {
async onRequest({ request, endResponse, fetchAPI }) {
const apiKey = await request.headers.get("x-api-key");
if (environment === "production") {
if (!apiKey || apiKey == null) {
endResponse(
new fetchAPI.Response(
JSON.stringify({
message: "No API Key provided"
}),
{ status: 401, headers: { "Content-Type": "application/json" } }
)
);
}
if (apiKey !== null && !(await isValidApiKey(apiKey))) {
endResponse(
new fetchAPI.Response(
JSON.stringify({
message: "Invalid API Key"
}),
{ status: 403, headers: { "Content-Type": "application/json" } }
)
);
}
}
}
};
}
const { handleRequest } = createYoga<NextContext>({
schema: createSchema({
typeDefs: typeDefs,
@@ -16,7 +59,10 @@ const { handleRequest } = createYoga<NextContext>({
graphqlEndpoint: "/api/graphql",
// Yoga needs to know how to create a valid Next response
fetchAPI: { Response }
fetchAPI: { Response },
plugins: [useApiKey(), EnvelopArmorPlugin()],
graphiql: environment !== "production" ? true : false
});
export {

View File

@@ -20,9 +20,15 @@ export default function RootLayout({
isClient: typeof window !== "undefined"
});
const client = createClient({
url: process.env.NEXT_PUBLIC_GRAPHQL_URL || "",
url: "/api/graphql",
exchanges: [cacheExchange, ssr, fetchExchange],
suspense: true
suspense: true,
fetchOptions: {
headers: {
"x-api-key":
process.env?.API_TOKEN || process.env?.NEXT_PUBLIC_API_TOKEN || ""
}
}
});
return [client, ssr];

View File

@@ -85,14 +85,10 @@ export default function Home() {
);
useEffect(() => {
if (!thirtyDayStatsFetching && thirtyDayStats.getStatsRange) {
if (!thirtyDayStatsFetching && thirtyDayStats && !thirtyDayStatsError) {
setLineChartArrState(lineChartArr(thirtyDayStats.getStatsRange));
}
}, [
thirtyDayStats.getStatsRange,
thirtyDayStatsError,
thirtyDayStatsFetching
]);
}, [thirtyDayStats, thirtyDayStatsError, thirtyDayStatsFetching]);
return (
<Fragment>
@@ -152,6 +148,7 @@ export default function Home() {
</VStack>
</VStack>
<VStack gap={10} w="100%">
{totalGroups ? (
<StatsList
title="Total Stats"
loading={totalStatsFetching || totalGroupsFetching}
@@ -161,6 +158,18 @@ export default function Home() {
commands={totalStats.getTotalStats.commandResponses}
triggers={totalStats.getTotalStats.timesTriggered}
/>
) : (
<StatsList
title="Total Stats"
loading={totalStatsFetching || totalGroupsFetching}
error={totalStatsError || totalGroupsError}
groups={0}
links={0}
commands={0}
triggers={0}
/>
)}
{todayStats ? (
<StatsList
title="Today's Stats"
loading={todaysStatsFetching}
@@ -169,6 +178,16 @@ export default function Home() {
commands={todayStats.getTodayStats.commandResponses}
triggers={todayStats.getTodayStats.timesTriggered}
/>
) : (
<StatsList
title="Today's Stats"
loading={todaysStatsFetching}
error={todaysStatsError}
links={0}
commands={0}
triggers={0}
/>
)}
</VStack>
<VStack w="95%" gap="5vh">
<VStack gap={1}>

143
yarn.lock
View File

@@ -458,6 +458,18 @@ __metadata:
languageName: node
linkType: hard
"@envelop/core@npm:^5.2.3":
version: 5.4.0
resolution: "@envelop/core@npm:5.4.0"
dependencies:
"@envelop/instrumentation": "npm:^1.0.0"
"@envelop/types": "npm:^5.2.1"
"@whatwg-node/promise-helpers": "npm:^1.2.4"
tslib: "npm:^2.5.0"
checksum: 10c0/e8f87c68ce80984f0127afe8c6786468b18db91e8cb2335468dbef8c8b32d0ff10121dc53dde56d6f6acbcad74798807258799864c398d15bd547c49839445d1
languageName: node
linkType: hard
"@envelop/core@npm:^5.3.0":
version: 5.3.2
resolution: "@envelop/core@npm:5.3.2"
@@ -672,6 +684,134 @@ __metadata:
languageName: node
linkType: hard
"@escape.tech/graphql-armor-block-field-suggestions@npm:3.0.1":
version: 3.0.1
resolution: "@escape.tech/graphql-armor-block-field-suggestions@npm:3.0.1"
dependencies:
"@envelop/core": "npm:^5.2.3"
graphql: "npm:^16.10.0"
dependenciesMeta:
"@envelop/core":
optional: true
checksum: 10c0/b90c32530d6d7ad5bf713f3f6f864d214fbdf9b3930eead5511009f2926a3804ed3c9c6bbe08e12db794e9fe4745aeb620b4e96fd3416c958c1d2580e2d19e1a
languageName: node
linkType: hard
"@escape.tech/graphql-armor-cost-limit@npm:2.4.3":
version: 2.4.3
resolution: "@escape.tech/graphql-armor-cost-limit@npm:2.4.3"
dependencies:
"@envelop/core": "npm:^5.2.3"
"@escape.tech/graphql-armor-types": "npm:0.7.0"
graphql: "npm:^16.10.0"
dependenciesMeta:
"@envelop/core":
optional: true
"@escape.tech/graphql-armor-types":
optional: true
checksum: 10c0/89dc54b68a29d572b98be66f9e4629a51c78d0c72f8b62aa5bbe071356bed7951611f226e71bb9bf1f83e322585f7b51982e454a49b0cd4f81c7fe4c0b822893
languageName: node
linkType: hard
"@escape.tech/graphql-armor-max-aliases@npm:2.6.2":
version: 2.6.2
resolution: "@escape.tech/graphql-armor-max-aliases@npm:2.6.2"
dependencies:
"@envelop/core": "npm:^5.2.3"
"@escape.tech/graphql-armor-types": "npm:0.7.0"
graphql: "npm:^16.10.0"
dependenciesMeta:
"@envelop/core":
optional: true
"@escape.tech/graphql-armor-types":
optional: true
checksum: 10c0/7a49bfa9faa68a4a8fad7beab40f7ccc6dc01d28dc6238a335e77aa0fc1ff1aab7a2207d11e5341b7c48e473a485ed2cdc98bd7d134bc8ae157945d77675bdb5
languageName: node
linkType: hard
"@escape.tech/graphql-armor-max-depth@npm:2.4.2":
version: 2.4.2
resolution: "@escape.tech/graphql-armor-max-depth@npm:2.4.2"
dependencies:
"@envelop/core": "npm:^5.2.3"
"@escape.tech/graphql-armor-types": "npm:0.7.0"
graphql: "npm:^16.10.0"
dependenciesMeta:
"@envelop/core":
optional: true
"@escape.tech/graphql-armor-types":
optional: true
checksum: 10c0/e38612e5bb0ed5db1d36100ca0701d32cc22a28f0418b56106a756683d86c1eef56d6987965748f1dbf386c19f7d693ae6a9b1416104cc63795e3dcb6ee0e27a
languageName: node
linkType: hard
"@escape.tech/graphql-armor-max-directives@npm:2.3.1":
version: 2.3.1
resolution: "@escape.tech/graphql-armor-max-directives@npm:2.3.1"
dependencies:
"@envelop/core": "npm:^5.2.3"
"@escape.tech/graphql-armor-types": "npm:0.7.0"
graphql: "npm:^16.10.0"
dependenciesMeta:
"@envelop/core":
optional: true
"@escape.tech/graphql-armor-types":
optional: true
checksum: 10c0/2f14c1bc4356087db30fd4c83b762383999f70e7fd289f6954d26ae62790ffe65c5aa279cbcb08d4aca666e671306eb97c43a514ef574cc6d81dea33963fc048
languageName: node
linkType: hard
"@escape.tech/graphql-armor-max-tokens@npm:2.5.1":
version: 2.5.1
resolution: "@escape.tech/graphql-armor-max-tokens@npm:2.5.1"
dependencies:
"@envelop/core": "npm:^5.2.3"
"@escape.tech/graphql-armor-types": "npm:0.7.0"
graphql: "npm:^16.10.0"
dependenciesMeta:
"@envelop/core":
optional: true
"@escape.tech/graphql-armor-types":
optional: true
checksum: 10c0/db9e5cbdbd66a1aa031e2398fdf507bb9cb51321e9bb9c75dc668c6a250c6e7b5bc8d499487973184af0dd6fc2c150f88e008b1badfe01e6690397763765de7f
languageName: node
linkType: hard
"@escape.tech/graphql-armor-types@npm:0.7.0":
version: 0.7.0
resolution: "@escape.tech/graphql-armor-types@npm:0.7.0"
dependencies:
graphql: "npm:^16.0.0"
checksum: 10c0/555d8ce8c364b31dfd8239cfb3cac3b44307950df457ce7f3585aa30d112edb9332ea6aa417f9e9fb245ed913004f8350ba9abbc60847fde75d4c006fd5c68b7
languageName: node
linkType: hard
"@escape.tech/graphql-armor@npm:^3.1.7":
version: 3.1.7
resolution: "@escape.tech/graphql-armor@npm:3.1.7"
dependencies:
"@escape.tech/graphql-armor-block-field-suggestions": "npm:3.0.1"
"@escape.tech/graphql-armor-cost-limit": "npm:2.4.3"
"@escape.tech/graphql-armor-max-aliases": "npm:2.6.2"
"@escape.tech/graphql-armor-max-depth": "npm:2.4.2"
"@escape.tech/graphql-armor-max-directives": "npm:2.3.1"
"@escape.tech/graphql-armor-max-tokens": "npm:2.5.1"
graphql: "npm:^16.10.0"
peerDependencies:
"@apollo/server": ^4.0.0
"@envelop/core": ^5.0.0
"@escape.tech/graphql-armor-types": 0.7.0
peerDependenciesMeta:
"@apollo/server":
optional: true
"@envelop/core":
optional: true
"@escape.tech/graphql-armor-types":
optional: true
checksum: 10c0/0795987bbfc1d79e22c1701fd53ef8a6b3da3b0bb03b1881f738040173a0c0e676d7a907076ca2a5827b1f1bf8e541b34f6c18d2600dac38cf9c7c6ea97aa9ac
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.7.0, @eslint-community/eslint-utils@npm:^4.8.0":
version: 4.9.0
resolution: "@eslint-community/eslint-utils@npm:4.9.0"
@@ -4997,7 +5137,7 @@ __metadata:
languageName: node
linkType: hard
"graphql@npm:^16.12.0":
"graphql@npm:^16.0.0, graphql@npm:^16.10.0, graphql@npm:^16.12.0":
version: 16.12.0
resolution: "graphql@npm:16.12.0"
checksum: 10c0/b6fffa4e8a4e4a9933ebe85e7470b346dbf49050c1a482fac5e03e4a1a7bed2ecd3a4c97e29f04457af929464bc5e4f2aac991090c2f320111eef26e902a5c75
@@ -5947,6 +6087,7 @@ __metadata:
"@chakra-ui/charts": "npm:^3.30.0"
"@chakra-ui/react": "npm:^3.30.0"
"@emotion/react": "npm:^11.14.0"
"@escape.tech/graphql-armor": "npm:^3.1.7"
"@eslint/eslintrc": "npm:^3.3.3"
"@eslint/js": "npm:^9.39.1"
"@iconify/react": "npm:^6.0.2"