Compare commits

..

2 Commits

Author SHA1 Message Date
e09b93d075 merge
Some checks are pending
ci / build (push) Waiting to run
2024-05-27 14:36:44 -08:00
f6ce2138b9 figure out strapi with pnpm 2024-05-27 14:20:58 -08:00
69 changed files with 20263 additions and 15376 deletions

View File

@ -16,4 +16,14 @@ app.json
compose/
docker-compose.*
.vscode
charts/**/charts
charts/**/charts
packages/strapi/.tmp/
packages/strapi/.cache/
packages/strapi/.git/
packages/strapi/.env
packages/strapi/build/
packages/strapi/node_modules/
packages/strapi/data/
packages/strapi/backup

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
**/.env
*~
charts/**/charts
.envrc
compose/

View File

@ -31,6 +31,10 @@ tilt:
secrets:
kubectl --namespace futureporn delete secret frp --ignore-not-found
kubectl --namespace futureporn create secret generic frp \
--from-literal=token=${FRP_TOKEN}
kubectl --namespace futureporn delete secret scout --ignore-not-found
kubectl --namespace futureporn create secret generic scout \
--from-literal=recentsToken=${SCOUT_RECENTS_TOKEN} \
@ -69,6 +73,7 @@ secrets:
--from-literal=apiTokenSalt=${STRAPI_API_TOKEN_SALT} \
--from-literal=appKeys=${STRAPI_APP_KEYS} \
--from-literal=databaseUrl=postgres.futureporn.svc.cluster.local://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} \
--from-literal=databaseUrl=postgres.futureporn.svc.cluster.local://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB} \
--from-literal=jwtSecret=${STRAPI_JWT_SECRET} \
--from-literal=muxPlaybackRestrictionId=${MUX_PLAYBACK_RESTRICTION_ID} \
--from-literal=muxSigningKeyPrivateKey=${MUX_SIGNING_KEY_PRIVATE_KEY} \
@ -83,7 +88,9 @@ secrets:
--from-literal=cdnBucketUscUrl=${CDN_BUCKET_USC_URL} \
--from-literal=transferTokenSalt=${TRANSFER_TOKEN_SALT}
kubectl --namespace futureporn delete secret realtime --ignore-not-found
kubectl --namespace futureporn create secret generic realtime \
--from-literal=postgresRealtimeConnectionString=${POSTGRES_REALTIME_CONNECTION_STRING}
define _script
cat <<'EOF' | ctlptl apply -f -
@ -102,6 +109,19 @@ minikube:
minikube addons enable metrics-server
kind:
bash -x ./scripts/kind-with-local-registry.sh
deps:
sudo pamac install make entr nvm minikube kubectl docker helm
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
echo "go to https://github.com/txn2/kubefwd/releases/latest to get kubefwd"
echo "go to https://github.com/tilt-dev/ctlptl/releases/latest to get ctlptl"
sudo systemctl enable docker
sudo systemctl start docker
usermod -aG docker cj
newgrp docker
# A gitea act runner which runs locally
# https://docs.gitea.com/next/usage/actions/overview

View File

@ -23,7 +23,7 @@ spec:
memory: 512Mi
image: jmalloc/echo-server
ports:
- name: http-port
- name: http
containerPort: 8080
---
@ -36,47 +36,47 @@ metadata:
app.kubernetes.io/name: echo
spec:
ports:
- name: http-port
- name: http
port: 8080
targetPort: http-port
targetPort: http
protocol: TCP
selector:
app: echo-server
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ngrok
namespace: futureporn
annotations:
kubernetes.io/ingress.class: ngrok
k8s.ngrok.com/namespace: futureporn
k8s.ngrok.com/service: ngrok
spec:
ingressClassName: ngrok
tls:
- secretName: ngrok-tls
hosts:
- "{{ .Values.ngrok.hostname }}"
rules:
- host: "{{ .Values.ngrok.hostname }}"
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: echo
port:
number: 8080
- path: /game
pathType: Prefix
backend:
service:
name: game-2048
port:
number: 8080
# ---
# apiVersion: networking.k8s.io/v1
# kind: Ingress
# metadata:
# name: ngrok
# namespace: futureporn
# annotations:
# kubernetes.io/ingress.class: ngrok
# k8s.ngrok.com/namespace: futureporn
# k8s.ngrok.com/service: ngrok
# spec:
# ingressClassName: ngrok
# tls:
# - secretName: ngrok-tls
# hosts:
# - "{{ .Values.ngrok.hostname }}"
# rules:
# - host: "{{ .Values.ngrok.hostname }}"
# http:
# paths:
# - path: /echo
# pathType: Prefix
# backend:
# service:
# name: echo
# port:
# number: 8080
# - path: /game
# pathType: Prefix
# backend:
# service:
# name: game-2048
# port:
# number: 8080
# - path: /strapi
# pathType: Prefix
# backend:

View File

@ -1,6 +1,6 @@
{{ if eq .Values.managedBy "Helm" }}
---
apiVersion: v1
kind: ServiceAccount
metadata:
@ -32,7 +32,8 @@ roleRef:
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
namespace: futureporn
---
apiVersion: apps/v1
kind: Deployment

View File

@ -0,0 +1,34 @@
{{ if eq .Values.managedBy "tilt" }}
---
apiVersion: frp.zufardhiyaulhaq.com/v1alpha1
kind: Client
metadata:
name: client-01
namespace: futureporn
spec:
server:
host: 155.138.254.201
port: 7000
authentication:
token:
secret:
name: frp
key: token
---
apiVersion: frp.zufardhiyaulhaq.com/v1alpha1
kind: Upstream
metadata:
name: echo
namespace: futureporn
spec:
client: client-01
tcp:
host: echo.futureporn.svc.cluster.local
port: 8080
server:
port: 8080
proxyProtocol: v2
{{ end }}

View File

@ -0,0 +1,47 @@
{{ if eq .Values.managedBy "tilt" }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ngrok
namespace: futureporn
annotations:
kubernetes.io/ingress.class: ngrok
k8s.ngrok.com/namespace: futureporn
k8s.ngrok.com/service: ngrok
spec:
ingressClassName: ngrok
rules:
- host: "{{ .Values.ngrok.hostname }}"
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: echo
port:
number: 8080
- path: /next
pathType: Prefix
backend:
service:
name: next
port:
number: 3000
- path: /strapi
pathType: Prefix
backend:
service:
name: strapi
port:
number: 1337
# - path: /snake
# pathType: Prefix
# backend:
# service:
# name: snake
# port:
# number: 8080
{{ end }}

View File

@ -0,0 +1,35 @@
apiVersion: v1
kind: Service
metadata:
name: snake
namespace: futureporn
spec:
ports:
- name: http
port: 8080
targetPort: 8080
selector:
app: snake
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: snake
namespace: futureporn
spec:
replicas: 2
selector:
matchLabels:
app: snake
template:
metadata:
labels:
app: snake
spec:
containers:
- name: snake
image: thoschu/de.schulte360.web.snake
ports:
- name: http
containerPort: 8080

1
charts/fp/templates/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
strapi-app.yaml

View File

@ -27,7 +27,8 @@ spec:
- name: HOSTNAME
value: 0.0.0.0
ports:
- containerPort: 3000
- name: web
containerPort: 3000
resources: {}
restartPolicy: OnFailure

View File

@ -0,0 +1,101 @@
# In development, we need a piko agent
{{ if eq .Values.managedBy "tilt" }}
{{ end }}
# In production, we need a piko server
{{ if eq .Values.managedBy "Helm" }}
---
apiVersion: v1
kind: Service
metadata:
name: piko
namespace: futureporn
labels:
app: piko
spec:
ports:
- port: 8000
name: proxy
- port: 8001
name: upstream
- port: 8002
name: admin
- port: 8003
name: gossip
clusterIP: None
selector:
app: piko
---
apiVersion: v1
kind: ConfigMap
metadata:
name: server-config
data:
server.yaml: |
cluster:
node_id_prefix: ${POD_NAME}-
join:
- piko
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: piko
spec:
selector:
matchLabels:
app: piko
serviceName: "piko"
replicas: 3
template:
metadata:
labels:
app: piko
spec:
terminationGracePeriodSeconds: 60
containers:
- name: piko
image: my-repo/piko:latest
ports:
- containerPort: 8000
name: proxy
- containerPort: 8001
name: upstream
- containerPort: 8002
name: admin
- containerPort: 8003
name: gossip
args:
- server
- --config.path
- /config/server.yaml
- --config.expand-env
resources:
limits:
cpu: 250m
ephemeral-storage: 1Gi
memory: 1Gi
requests:
cpu: 250m
ephemeral-storage: 1Gi
memory: 1Gi
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: config
mountPath: "/config"
readOnly: true
volumes:
- name: config
configMap:
name: server-config
items:
- key: "server.yaml"
path: "server.yaml"
{{ end }}

View File

@ -7,7 +7,7 @@ annotations:
tilt.dev/down-policy: keep
spec:
accessModes:
- ReadWriteOncePod
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
resources:
requests:

View File

@ -0,0 +1,108 @@
apiVersion: v1
kind: Service
metadata:
name: scout
namespace: futureporn
spec:
selector:
app.kubernetes.io/name: scout
ports:
- name: web
port: 3000
targetPort: 3000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: scout
namespace: futureporn
labels:
app: scout
spec:
replicas: 1
selector:
matchLabels:
app: scout
template:
metadata:
labels:
app: scout
spec:
containers:
- name: scout
image: "{{ .Values.scout.containerName }}"
ports:
- containerPort: 5000
env:
- name: POSTGRES_REALTIME_CONNECTION_STRING
valueFrom:
secretKeyRef:
name: realtime
key: postgresRealtimeConnectionString
- name: STRAPI_URL
value: https://strapi.futureporn.svc.cluster.local
- name: SCOUT_RECENTS_TOKEN
valueFrom:
secretKeyRef:
name: scout
key: recentsToken
- name: SCOUT_IMAP_SERVER
valueFrom:
secretKeyRef:
name: scout
key: imapServer
- name: SCOUT_IMAP_PORT
valueFrom:
secretKeyRef:
name: scout
key: imapPort
- name: SCOUT_IMAP_USERNAME
valueFrom:
secretKeyRef:
name: scout
key: imapUsername
- name: SCOUT_IMAP_PASSWORD
valueFrom:
secretKeyRef:
name: scout
key: imapPassword
- name: SCOUT_IMAP_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: scout
key: imapAccessToken
- name: SCOUT_STRAPI_API_KEY
valueFrom:
secretKeyRef:
name: scout
key: strapiApiKey
{{ if eq .Values.managedBy "Helm" }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: scout
namespace: futureporn
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
tls:
- secretName: scout-tls
hosts:
- scout.sbtp.xyz
rules:
- host: scout.sbtp.xyz
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: scout
port:
number: 3000
{{ end }}

View File

@ -7,9 +7,10 @@ spec:
selector:
app.kubernetes.io/name: strapi
ports:
- name: web
port: 1337
targetPort: 1337
- name: http
port: 1339
targetPort: http
protocol: TCP
---
apiVersion: v1
@ -24,7 +25,8 @@ spec:
- name: strapi
image: "{{ .Values.strapi.containerName }}"
ports:
- containerPort: 1337
- name: http
containerPort: 1339
env:
- name: ADMIN_JWT_SECRET
valueFrom:
@ -120,9 +122,9 @@ spec:
value: "{{ .Values.strapi.port }}"
resources:
limits:
cpu: 500m
memory: 1Gi
restartPolicy: OnFailure
cpu: 1000m
memory: 2Gi
restartPolicy: Always
# ---
@ -160,7 +162,7 @@ spec:
ingressClassName: "{{ .Values.strapi.ingressClassName }}"
backend:
serviceName: strapi
servicePort: 1337
servicePort: 1339
tls:
- secretName: strapi-tls
hosts:
@ -175,6 +177,6 @@ spec:
service:
name: strapi
port:
number: 1337
number: 1339
{{ end }}

View File

@ -1,5 +1,5 @@
storageClassName: csi-hostpath-sc # used by minikube
# storageClassName: standard # used by Kind
# storageClassName: csi-hostpath-sc # used by minikube
storageClassName: standard # used by Kind
managedBy: tilt
link2cid:
containerName: fp/link2cid
@ -9,18 +9,25 @@ next:
hostname: next.futureporn.svc.cluster.local
capture:
containerName: fp/capture
scout:
containerName: fp/scout
pubsubServerUrl: https://realtime.futureporn.svc.cluster.local/faye
certIssuer: letsencrypt-staging
hostname: next.futureporn.svc.cluster.local
capture:
containerName: fp/capture
scout:
containerName: fp/scout
pubsubServerUrl: https://realtime.futureporn.svc.cluster.local/faye
strapi:
containerName: fp/strapi
port: 1337
url: https://strapi.futureporn.svc.cluster.local
port: 1339
url: http://localhost:1339
certIssuer: letsencrypt-staging
hostname: strapi.futureporn.svc.cluster.local
ingressClassName: ngrok
ngrok:
hostname: prawn-sweeping-muskrat.ngrok-free.app
hostname: grateful-engaging-cicada.ngrok-free.app
realtime:
containerName: fp/realtime
adminEmail: cj@futureporn.net

View File

@ -12,7 +12,7 @@ capture:
containerName: gitea.futureporn.net/futureporn/capture:latest
strapi:
containerName: sjc.vultrcr.com/fpcontainers/strapi
port: 1337
port: 1339
url: https://portal.futureporn.net
certIssuer: letsencrypt-prod
hostname: strapi.sbtp.xyz

View File

@ -1,17 +1,26 @@
FROM node:20-alpine
WORKDIR /app
FROM node:20-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN apk update
FROM base AS build
ENV NODE_ENV=production
COPY pnpm-lock.yaml ./
RUN pnpm fetch
COPY ./packages/scout /app
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN mkdir -p /prod/scout
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN ls -la ./packages
RUN pnpm deploy --filter=@futureporn/scout --prod /prod/scout
# COPY pnpm-lock.yaml ./
# RUN pnpm fetch
# COPY ./packages/scout /app
FROM base AS scout
COPY --from=build /prod/scout /app
WORKDIR /app
RUN ls -la
ENTRYPOINT ["pnpm"]
CMD ["run", "start"]

19
d.strapi-app.dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM node:18-alpine3.18
# Installing libvips-dev for sharp Compatibility
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev nasm bash vips-dev git
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
WORKDIR /opt/
COPY ./packages/strapi-app/package.json ./packages/strapi-app/yarn.lock ./
RUN yarn global add node-gyp
RUN yarn config set network-timeout 600000 -g && yarn install
ENV PATH /opt/node_modules/.bin:$PATH
WORKDIR /opt/app
COPY ./packages/strapi-app/ .
RUN chown -R node:node /opt/app
USER node
RUN ["yarn", "build"]
EXPOSE 1338
CMD ["yarn", "develop", "--debug"]

View File

@ -1,34 +1,24 @@
FROM node:20-alpine as base
WORKDIR /app
FROM node:18-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev vips-dev libc6-compat git nasm bash gcompat
# Installing libvips-dev for sharp Compatibility
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev nasm bash vips-dev git
ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}
FROM base AS install
COPY ./packages/strapi/pnpm-lock.yaml ./packages/strapi/package.json ./
RUN pnpm install --prod --shamefully-hoist && pnpm run build
COPY ./packages/strapi .
FROM base AS build
WORKDIR /usr/src/app/
COPY ./packages/strapi/package.json ./packages/strapi/pnpm-lock.yaml .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY ./packages/strapi/ .
FROM build AS dev
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY --from=build /usr/src/app/ .
RUN chown -R node:node /app
USER node
# FROM base AS install
# COPY ./packages/strapi/pnpm-lock.yaml ./
# RUN pnpm fetch --prod
# COPY ./packages/strapi .
# RUN pnpm i --offline --prod --shamefully-hoist && pnpm run build
# RUN chown -R node:node /app
# USER node
FROM install AS dev
ENV NODE_ENV=development
ENTRYPOINT ["pnpm"]
CMD ["run", "dev"]
FROM install AS release
ENV NODE_ENV=production
ENTRYPOINT ["pnpm"]
CMD ["run", "start"]
RUN ["pnpm", "run", "build"]
EXPOSE 1339
CMD ["pnpm", "run", "dev"]

View File

@ -1,14 +0,0 @@
# Reference-- https://pnpm.io/docker
FROM node:20-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
COPY ./packages/link2cid/package.json /app
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod
COPY ./packages/link2cid/index.js /app
COPY ./packages/link2cid/src /app/src
ENTRYPOINT ["pnpm"]
CMD ["start"]

View File

@ -1,65 +0,0 @@
## Important! Build context is the ROOT of the project.
## this keeps the door open for future possibility of shared code between pnpm workspace packages
FROM node:20-slim AS base
FROM base AS deps
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app
FROM deps AS install
ARG NEXT_PUBLIC_SITE_URL=https://futureporn.net
ARG NEXT_PUBLIC_STRAPI_URL=https://portal.futureporn.net
ARG NEXT_PUBLIC_UPPY_COMPANION_URL=https://uppy.futureporn.net
ENV NEXT_PUBLIC_SITE_URL ${NEXT_PUBLIC_SITE_URL}
ENV NEXT_PUBLIC_STRAPI_URL ${NEXT_PUBLIC_STRAPI_URL}
ENV NEXT_PUBLIC_UPPY_COMPANION_URL ${NEXT_PUBLIC_UPPY_COMPANION_URL}
ENV NEXT_TELEMETRY_DISABLED 1
COPY pnpm-lock.yaml ./
RUN pnpm fetch
COPY ./packages/next /app
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store pnpm install
FROM install AS dev
CMD ["pnpm", "run", "dev"]
FROM install AS build
RUN pnpm run build
# COPY --chown=node:node --from=install /app/package.json /app/pnpm-lock.yaml ./
# RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
# COPY --from=install /app /app # i think this is duplicate
# can't get these to work because errors like "/prod/next/.next/standalone": not found
# as if pnpm is not copying the build artifacts.
# also this makes the build REALLY slow (adds ~10mins to build time)
# RUN pnpm deploy --filter=@futureporn/next --prod /prod/next
# RUN pnpm deploy --filter=@futureporn/link2cid --prod /prod/link2cid
# FROM deps as release
# # ENV NEXT_SHARP_PATH=/app/node_modules/sharp
# ENV NODE_ENV=production
# WORKDIR /app
# COPY --from=build /app/public ./public
# COPY --from=build /app/.next/standalone ./
# COPY --from=build /app/.next/static ./.next/static
# CMD [ "dumb-init", "node", "server.js" ]
FROM deps AS next
RUN apt-get update && apt-get install -y -qq --no-install-recommends dumb-init
COPY --chown=node:node --from=build /app/package.json /app/pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile
COPY --chown=node:node --from=build /app/public ./public
COPY --chown=node:node --from=build /app/.next/standalone ./
COPY --chown=node:node --from=build /app/.next/static ./.next/static
ENV TZ=UTC
ENV NODE_ENV=production
ENV HOSTNAME="0.0.0.0"
CMD [ "dumb-init", "node", "server.js" ]

File diff suppressed because it is too large Load Diff

View File

@ -79,7 +79,6 @@ async function init () {
const appEnv = new Array(
'FUTUREPORN_WORKDIR',
'PUBSUB_SERVER_URL',
'DOWNLOADER_UA',
'PORT'
)
@ -98,7 +97,6 @@ async function init () {
logger,
pkg: JSON.parse(fs.readFileSync('./package.json', { encoding: 'utf-8'})),
workerId: `${os.hostname}-${createId()}`,
pubsub: new faye.Client(process.env.PUBSUB_SERVER_URL)
};
await initFastify(appContext);
@ -119,7 +117,6 @@ async function main () {
appContext.logger.log({ level: 'info', message: `capture version: ${appContext.pkg.version}` })
appContext.logger.log({ level: 'info', message: `my capture directory is ${appContext.env.FUTUREPORN_WORKDIR}` })
appContext.logger.log({ level: 'info', message: `listening for faye signals from ${appContext.env.PUBSUB_SERVER_URL}` })
// connect to realtime server

2139
packages/capture/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
# Base Image
FROM golang:latest
# Set the Current Working Directory inside the container
WORKDIR /app
# Copy everything from the current directory to the PWD(Present Working Directory) inside the container
COPY . .
# Download all the dependencies
RUN go mod download
# Build the Go app
RUN go build -o main .
# Expose port 8080 to the outside world
EXPOSE 8080
# Command to run the executable
CMD ["./main"]

View File

@ -1,13 +0,0 @@
package main
import "net/http"
func main() {
http.HandleFunc("/", hellowordhandler)
http.ListenAndServe(":9000", nil)
}
func hellowordhandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
}

View File

@ -1,2 +0,0 @@
node_modules
README.md

View File

@ -1,36 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -1,12 +0,0 @@
FROM node:20
RUN corepack enable
WORKDIR /app
ADD package.json .
ADD package-lock.json .
RUN pnpm install
ADD . .
ENTRYPOINT [ "pnpm", "next", "dev" ]

View File

@ -1,36 +0,0 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View File

@ -1,107 +0,0 @@
:root {
--max-width: 1100px;
--border-radius: 12px;
--font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
"Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
"Fira Mono", "Droid Sans Mono", "Courier New", monospace;
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
--primary-glow: conic-gradient(
from 180deg at 50% 50%,
#16abff33 0deg,
#0885ff33 55deg,
#54d6ff33 120deg,
#0071ff33 160deg,
transparent 360deg
);
--secondary-glow: radial-gradient(
rgba(255, 255, 255, 1),
rgba(255, 255, 255, 0)
);
--tile-start-rgb: 239, 245, 249;
--tile-end-rgb: 228, 232, 233;
--tile-border: conic-gradient(
#00000080,
#00000040,
#00000030,
#00000020,
#00000010,
#00000010,
#00000080
);
--callout-rgb: 238, 240, 241;
--callout-border-rgb: 172, 175, 176;
--card-rgb: 180, 185, 188;
--card-border-rgb: 131, 134, 135;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
--primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
--secondary-glow: linear-gradient(
to bottom right,
rgba(1, 65, 255, 0),
rgba(1, 65, 255, 0),
rgba(1, 65, 255, 0.3)
);
--tile-start-rgb: 2, 13, 46;
--tile-end-rgb: 2, 5, 19;
--tile-border: conic-gradient(
#ffffff80,
#ffffff40,
#ffffff30,
#ffffff20,
#ffffff10,
#ffffff10,
#ffffff80
);
--callout-rgb: 20, 20, 20;
--callout-border-rgb: 108, 108, 108;
--card-rgb: 100, 100, 100;
--card-border-rgb: 200, 200, 200;
}
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
a {
color: inherit;
text-decoration: none;
}
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}

View File

@ -1,17 +0,0 @@
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}

View File

@ -1,95 +0,0 @@
import Image from "next/image";
import styles from "./page.module.css";
export default function Home() {
return (
<main className={styles.main}>
<div className={styles.description}>
<p>
Get started by editing&nbsp;
<code className={styles.code}>app/page.js</code>
</p>
<div>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{" "}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className={styles.vercelLogo}
width={100}
height={24}
priority
/>
</a>
</div>
</div>
<div className={styles.center}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
</div>
<div className={styles.grid}>
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Docs <span>-&gt;</span>
</h2>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Learn <span>-&gt;</span>
</h2>
<p>Learn about Next.js in an interactive course with&nbsp;quizzes!</p>
</a>
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Templates <span>-&gt;</span>
</h2>
<p>Explore starter templates for Next.js.</p>
</a>
<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Deploy <span>-&gt;</span>
</h2>
<p>
Instantly deploy your Next.js site to a shareable URL with Vercel.
</p>
</a>
</div>
</main>
);
}

View File

@ -1,230 +0,0 @@
.main {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 6rem;
min-height: 100vh;
}
.description {
display: inherit;
justify-content: inherit;
align-items: inherit;
font-size: 0.85rem;
max-width: var(--max-width);
width: 100%;
z-index: 2;
font-family: var(--font-mono);
}
.description a {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
.description p {
position: relative;
margin: 0;
padding: 1rem;
background-color: rgba(var(--callout-rgb), 0.5);
border: 1px solid rgba(var(--callout-border-rgb), 0.3);
border-radius: var(--border-radius);
}
.code {
font-weight: 700;
font-family: var(--font-mono);
}
.grid {
display: grid;
grid-template-columns: repeat(4, minmax(25%, auto));
max-width: 100%;
width: var(--max-width);
}
.card {
padding: 1rem 1.2rem;
border-radius: var(--border-radius);
background: rgba(var(--card-rgb), 0);
border: 1px solid rgba(var(--card-border-rgb), 0);
transition: background 200ms, border 200ms;
}
.card span {
display: inline-block;
transition: transform 200ms;
}
.card h2 {
font-weight: 600;
margin-bottom: 0.7rem;
}
.card p {
margin: 0;
opacity: 0.6;
font-size: 0.9rem;
line-height: 1.5;
max-width: 30ch;
text-wrap: balance;
}
.center {
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding: 4rem 0;
}
.center::before {
background: var(--secondary-glow);
border-radius: 50%;
width: 480px;
height: 360px;
margin-left: -400px;
}
.center::after {
background: var(--primary-glow);
width: 240px;
height: 180px;
z-index: -1;
}
.center::before,
.center::after {
content: "";
left: 50%;
position: absolute;
filter: blur(45px);
transform: translateZ(0);
}
.logo {
position: relative;
}
/* Enable hover only on non-touch devices */
@media (hover: hover) and (pointer: fine) {
.card:hover {
background: rgba(var(--card-rgb), 0.1);
border: 1px solid rgba(var(--card-border-rgb), 0.15);
}
.card:hover span {
transform: translateX(4px);
}
}
@media (prefers-reduced-motion) {
.card:hover span {
transform: none;
}
}
/* Mobile */
@media (max-width: 700px) {
.content {
padding: 4rem;
}
.grid {
grid-template-columns: 1fr;
margin-bottom: 120px;
max-width: 320px;
text-align: center;
}
.card {
padding: 1rem 2.5rem;
}
.card h2 {
margin-bottom: 0.5rem;
}
.center {
padding: 8rem 0 6rem;
}
.center::before {
transform: none;
height: 300px;
}
.description {
font-size: 0.8rem;
}
.description a {
padding: 1rem;
}
.description p,
.description div {
display: flex;
justify-content: center;
position: fixed;
width: 100%;
}
.description p {
align-items: center;
inset: 0 0 auto;
padding: 2rem 1rem 1.4rem;
border-radius: 0;
border: none;
border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
background: linear-gradient(
to bottom,
rgba(var(--background-start-rgb), 1),
rgba(var(--callout-rgb), 0.5)
);
background-clip: padding-box;
backdrop-filter: blur(24px);
}
.description div {
align-items: flex-end;
pointer-events: none;
inset: auto 0 0;
padding: 2rem;
height: 200px;
background: linear-gradient(
to bottom,
transparent 0%,
rgb(var(--background-end-rgb)) 40%
);
z-index: 1;
}
}
/* Tablet and Smaller Desktop */
@media (min-width: 701px) and (max-width: 1120px) {
.grid {
grid-template-columns: repeat(2, 50%);
}
}
@media (prefers-color-scheme: dark) {
.vercelLogo {
filter: invert(1);
}
.logo {
filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
}
}
@keyframes rotate {
from {
transform: rotate(360deg);
}
to {
transform: rotate(0deg);
}
}

View File

@ -1,7 +0,0 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}

View File

@ -1,4 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default nextConfig;

View File

@ -1,389 +0,0 @@
{
"name": "helloworldy",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "helloworldy",
"version": "0.1.0",
"dependencies": {
"next": "14.1.3",
"react": "^18",
"react-dom": "^18"
}
},
"node_modules/@next/env": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.3.tgz",
"integrity": "sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ=="
},
"node_modules/@next/swc-darwin-arm64": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.3.tgz",
"integrity": "sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.3.tgz",
"integrity": "sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.3.tgz",
"integrity": "sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.3.tgz",
"integrity": "sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.3.tgz",
"integrity": "sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.3.tgz",
"integrity": "sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.3.tgz",
"integrity": "sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.3.tgz",
"integrity": "sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.3.tgz",
"integrity": "sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
"integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
"dependencies": {
"streamsearch": "^1.1.0"
},
"engines": {
"node": ">=10.16.0"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001599",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz",
"integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
]
},
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/next": {
"version": "14.1.3",
"resolved": "https://registry.npmjs.org/next/-/next-14.1.3.tgz",
"integrity": "sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==",
"dependencies": {
"@next/env": "14.1.3",
"@swc/helpers": "0.5.2",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
"graceful-fs": "^4.2.11",
"postcss": "8.4.31",
"styled-jsx": "5.1.1"
},
"bin": {
"next": "dist/bin/next"
},
"engines": {
"node": ">=18.17.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "14.1.3",
"@next/swc-darwin-x64": "14.1.3",
"@next/swc-linux-arm64-gnu": "14.1.3",
"@next/swc-linux-arm64-musl": "14.1.3",
"@next/swc-linux-x64-gnu": "14.1.3",
"@next/swc-linux-x64-musl": "14.1.3",
"@next/swc-win32-arm64-msvc": "14.1.3",
"@next/swc-win32-ia32-msvc": "14.1.3",
"@next/swc-win32-x64-msvc": "14.1.3"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.3.0"
},
"peerDependenciesMeta": {
"@opentelemetry/api": {
"optional": true
},
"sass": {
"optional": true
}
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
},
"node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
},
"peerDependencies": {
"react": "^18.2.0"
}
},
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
"dependencies": {
"loose-envify": "^1.1.0"
}
},
"node_modules/source-map-js": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.1.0.tgz",
"integrity": "sha512-9vC2SfsJzlej6MAaMPLu8HiBSHGdRAJ9hVFYN1ibZoNkeanmDmLUcIrj6G9DGL7XMJ54AKg/G75akXl1/izTOw==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/styled-jsx": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
"integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
"dependencies": {
"client-only": "0.0.1"
},
"engines": {
"node": ">= 12.0.0"
},
"peerDependencies": {
"react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"babel-plugin-macros": {
"optional": true
}
}
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
}
}
}

View File

@ -1,16 +0,0 @@
{
"name": "helloworldy",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "14.1.3"
}
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 629 B

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ interface IPageParams {
export default async function Page ({ params: { cuid } }: IPageParams) {
const stream = await getStreamByCuid(cuid);
console.log(`getting stream by cuid. cuid=${cuid}`)
return (
<>
<StreamPage stream={stream} />

View File

@ -45,6 +45,8 @@ function determineStatus(stream: IStream): Status {
}
export default function StreamPage({ stream }: IStreamProps) {
console.log('StreamPage function has been invoked! stream as follows')
console.log(stream)
const displayName = stream.attributes.vtuber.data.attributes.displayName;
const date = new Date(stream.attributes.date);
const [hemisphere, setHemisphere] = useState(Hemisphere.NORTHERN);

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +1,26 @@
{
"name": "futureporn-scout",
"name": "@futureporn/scout",
"type": "module",
"version": "3.0.0",
"version": "3.3.0",
"description": "detect when a stream goes live",
"main": "index.js",
"scripts": {
"test": "mocha",
"start:email": "node ./src/index.email.js",
"start:browser": "node ./src/index.browser.js",
"start": "concurrently \"npm:start:email\""
"start": "node ./src/index.email.js",
"start:browser": "node ./src/index.browser.js"
},
"keywords": [],
"author": "@CJ_Clippy",
"license": "Unlicense",
"dependencies": {
"pg-pubsub": "workspace:*",
"cheerio": "1.0.0-rc.12",
"concurrently": "^8.2.2",
"dotenv": "^16.4.5",
"fastq": "^1.17.1",
"faye": "^1.4.0",
"imapflow": "^1.0.160",
"limiter": "^2.0.1",
"limiter": "2.0.1",
"mailparser": "^3.7.1",
"puppeteer": "^22.7.1",
"puppeteer-extra": "^3.3.6",
@ -28,6 +28,7 @@
"puppeteer-extra-plugin-stealth": "^2.11.2",
"xpath": "^0.0.34"
},
"packageManager": "pnpm@9.1.2",
"devDependencies": {
"chai": "^5.1.0",
"mocha": "^10.4.0"

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,10 @@ import { ImapFlow } from 'imapflow';
import EventEmitter from 'node:events';
import 'dotenv/config';
import { simpleParser } from 'mailparser';
import { RateLimiter } from 'limiter';
// pinned to v2.0.1 due to https://github.com/jhurliman/node-rate-limiter/issues/80
import * as $limiter from 'limiter';
const { RateLimiter } = $limiter

View File

@ -5,7 +5,7 @@
*/
import { checkEmail } from './parsers.js'
import { signalRealtime, createStreamInDb, pushToRecents } from './signals.js'
import { signalRealtime, createStreamInDb } from './signals.js'
import { Email } from './imap.js'
import fastq from "fastq";
@ -20,14 +20,13 @@ async function handleMessage({email, msg}) {
console.log(' ✏️ checking e-mail')
const { isMatch, url, platform, channel, displayName, date } = (await checkEmail(body))
console.log(' ✏️ signalling realtime')
if (isMatch) await signalRealtime(url);
if (isMatch) {
console.log(' ✏️✏️ signalling realtime')
await signalRealtime({ url, platform, channel, displayName, date })
// console.log(' ✏️ creating stream entry in db')
// if (isMatch) await createStreamInDb({ platform, channel, date });
console.log(' ✏️ pushToRecents')
if (platform === 'Fansly') await pushToRecents({ date, url, channel, displayName });
console.log(' ✏️✏️ creating stream entry in db')
await createStreamInDb({ platform, channel, date })
}
console.log(' ✏️ archiving e-mail')
await email.archiveMessage(msg.uid)

View File

@ -1,20 +1,36 @@
import 'dotenv/config'
import Faye from 'faye'
// import { PgPubSub } from '@imqueue/pg-pubsub'; // @see https://github.com/imqueue/pg-pubsub/issues/20
import { PgPubSub } from 'pg-pubsub'
// alternative js libraries for postgres notify/wait
// * https://github.com/imqueue/pg-pubsub
// * https://github.com/voxpelli/node-pg-pubsub
// * https://github.com/andywer/pg-listen
if (!process.env.PUBSUB_SERVER_URL) throw new Error('PUBSUB_SERVER_URL is missing from env');
if (!process.env.SCOUT_STRAPI_API_KEY) throw new Error('SCOUT_STRAPI_API_KEY is missing from env');
if (!process.env.STRAPI_URL) throw new Error('STRAPI_URL is missing from env');
if (!process.env.SCOUT_RECENTS_TOKEN) throw new Error('SCOUT_RECENTS_TOKEN is undefined in env');
if (!process.env.POSTGRES_REALTIME_CONNECTION_STRING) throw new Error('POSTGRES_REALTIME_CONNECTION_STRING is undefined in env');
console.log(`process.env.POSTGRES_REALTIME_CONNECTION_STRING=${process.env.POSTGRES_REALTIME_CONNECTION_STRING}`)
const faye = new Faye.Client(process.env.PUBSUB_SERVER_URL);
const pubSub = new PgPubSub({
connectionString: process.env.POSTGRES_REALTIME_CONNECTION_STRING,
// @see https://github.com/imqueue/pg-pubsub?tab=readme-ov-file#single-listener-inter-process-locking
singleListener: true
});
(async () => {
await pubSub.connect();
})();
export async function signalRealtime (url) {
faye.publish('/signals', {
signal: 'startV2',
url: url
})
export async function signalRealtime ({ url, platform, channel, displayName, date}) {
// faye.publish('/signals', {
// signal: 'startV2',
// url: url
// })
console.log(`📰📰📰 publishing streamStarted event. url=${url}, platform=${platform}, channel=${channel}, displayName=${displayName}, date=${date}`)
pubSub.notify('streamStarted', { url, platform, channel, displayName, date });
}
@ -22,7 +38,9 @@ export async function signalRealtime (url) {
* Create a database record which shows this stream exists
*/
export async function createStreamInDb ({ platform, channel, date }) {
const res = await fetch(`${process.env.STRAPI_URL}/api/streams`, {
const url = `${process.env.STRAPI_URL}/api/streams`
console.log(`we are going to fetch POST ${url} now.`)
const res = await fetch(url, {
method: 'POST',
headers: {
'authorization': `Bearer ${process.env.SCOUT_STRAPI_API_KEY}`
@ -36,32 +54,11 @@ export async function createStreamInDb ({ platform, channel, date }) {
}
})
})
const json = await res.json()
console.log('we got the json')
console.log(json)
}
/**
* this function is for whoisonline.sbtp.xyz
*
* this is a short-term thing, feel free to delete when it's no longer useful
*/
export async function pushToRecents ({ url, channel, displayName, date }) {
const lastSeen = new Date().toISOString()
console.log(`pushToRecents with url=${url} channel=${channel} displayName=${displayName} date=${date}`)
const res = await fetch('https://whoisonline.sbtp.xyz/recents', {
method: 'POST',
headers: {
'authorization': `Bearer ${process.env.SCOUT_RECENTS_TOKEN}`
},
body: JSON.stringify({
data: {
url,
channel,
displayName,
date
}
})
})
}

View File

@ -1,9 +1,3 @@
.tmp/
.cache/
.git/
.env
build/
node_modules/
# Ignoring folders that might be used in starter templates
data/
backup
# STOP! This .gitignore is probably not the .gitignore you are looking for.
# The gitignore in the ROOT of the Docker context is the .gitignore that docker uses.
# thus, see ../../.gitignore

View File

@ -1 +1 @@
lts/iron
lts/hydrogen

View File

@ -4,7 +4,7 @@
"version": "0.3.0",
"description": "A Strapi application",
"scripts": {
"dev": "pnpm strapi develop",
"dev": "strapi develop",
"dev:c": "concurrently \"npm:tunnel\" \"npm:dev:strapi\"",
"tunnel": "ngrok start futureporn-strapi",
"chisel": "bash ./chisel.sh",
@ -15,28 +15,27 @@
},
"dependencies": {
"@11ty/eleventy-fetch": "^4.0.1",
"@aws-sdk/client-s3": "^3.550.0",
"@aws-sdk/client-s3": "^3.583.0",
"@esm2cjs/execa": "6.1.1-cjs.1",
"@mux/mux-node": "^7.3.5",
"@paralleldrive/cuid2": "^2.2.2",
"@radix-ui/react-use-callback-ref": "^1.0.1",
"@strapi/plugin-i18n": "4.23.0",
"@strapi/plugin-users-permissions": "4.23.0",
"@strapi/provider-email-sendgrid": "4.23.0",
"@strapi/provider-upload-cloudinary": "4.23.0",
"@strapi/strapi": "4.23.0",
"@strapi/utils": "4.23.0",
"@strapi/plugin-i18n": "4.24.3",
"@strapi/plugin-users-permissions": "4.24.3",
"@strapi/provider-email-sendgrid": "4.24.3",
"@strapi/provider-upload-cloudinary": "4.24.3",
"@strapi/strapi": "4.24.3",
"@strapi/utils": "4.24.3",
"@testing-library/dom": "8.19.0",
"@testing-library/react": "12.1.4",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "14.4.3",
"aws-sdk": "^2.1595.0",
"aws-sdk": "^2.1628.0",
"bcryptjs": "2.4.3",
"better-sqlite3": "8.0.1",
"canvas": "^2.11.2",
"codemirror": "^6.0.1",
"css-loader": "^6.11.0",
"cuid": "^3.0.0",
"date-fns": "^3.6.0",
"formik": "2.2.9",
"fuzzy-search": "^3.2.1",
@ -45,7 +44,7 @@
"immer": "9.0.19",
"jsonwebtoken": "9.0.0",
"jwk-to-pem": "2.0.5",
"koa": "^2.15.2",
"koa": "^2.15.3",
"koa2-ratelimit": "^1.1.3",
"lodash": "4.17.21",
"match-sorter": "^4.2.1",
@ -55,18 +54,18 @@
"pg": "^8.11.5",
"prop-types": "^15.8.1",
"purest": "4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-intl": "6.3.2",
"react-query": "3.24.3",
"react-redux": "8.0.5",
"react-router-dom": "5.3.4",
"react-test-renderer": "^17.0.2",
"semver": "^7.6.0",
"react-test-renderer": "^18.3.1",
"semver": "^7.6.2",
"sharp": "0.33.3",
"strapi-plugin-fuzzy-search": "^2.2.1",
"styled-components": "5.3.3",
"typescript": "^5.4.4",
"typescript": "^4.7",
"url-join": "4.0.1",
"yallist": "^4.0.0",
"yup": "^0.32.11"
@ -77,16 +76,14 @@
"author": {
"name": "CJ_Clippy"
},
"strapi": {
"uuid": false
},
"packageManager": "pnpm@8.10.5",
"packageManager": "pnpm@9.1.3",
"engines": {
"node": "20.x.x",
"node": "18.x.x",
"npm": ">=6.0.0"
},
"license": "MIT",
"pnpm": {
"strapi": {
"uuid": false,
"supportedArchitectures": {
"libc": [
"musl"

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,9 @@ const { validateCreateUserBody, validateUpdateUserBody } = require('./validation
const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants;
console.log(`>>>> Hey, it's me. Your sleep paralysis demon. I wanted to take this moment to console.log contentTypes in this content-manager-user.js file, because I'm having to debug this little shit of a program, Strapi js. (contentTypes is as follows)`)
console.log(contentTypes)
const userModel = 'plugin::users-permissions.user';
const ACTIONS = {
read: 'plugin::content-manager.explorer.read',

File diff suppressed because it is too large Load Diff

6
pnpm-lock.yaml generated
View File

@ -1,5 +1,9 @@
lockfileVersion: '6.0'
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.: {}

View File

@ -1,6 +1,2 @@
packages:
- 'packages/uppy'
- 'packages/bot'
- 'packages/next'
- 'packages/strapi'
- 'packages/*'

View File

@ -0,0 +1,70 @@
#!/bin/sh
set -o errexit
# 1. Create registry container unless it already exists
reg_name='kind-registry'
reg_port='5001'
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
docker run \
-d --restart=always -p "127.0.0.1:${reg_port}:5000" --network bridge --name "${reg_name}" \
registry:2
fi
# 2. Create kind cluster with containerd registry config dir enabled
# TODO: kind will eventually enable this by default and this patch will
# be unnecessary.
#
# See:
# https://github.com/kubernetes-sigs/kind/issues/2875
# https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration
# See: https://github.com/containerd/containerd/blob/main/docs/hosts.md
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
- role: worker
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
EOF
# 3. Add the registry config to the nodes
#
# This is necessary because localhost resolves to loopback addresses that are
# network-namespace local.
# In other words: localhost in the container is not localhost on the host.
#
# We want a consistent name that works from both ends, so we tell containerd to
# alias localhost:${reg_port} to the registry container when pulling images
REGISTRY_DIR="/etc/containerd/certs.d/localhost:${reg_port}"
for node in $(kind get nodes); do
docker exec "${node}" mkdir -p "${REGISTRY_DIR}"
cat <<EOF | docker exec -i "${node}" cp /dev/stdin "${REGISTRY_DIR}/hosts.toml"
[host."http://${reg_name}:5000"]
EOF
done
# 4. Connect the registry to the cluster network if not already connected
# This allows kind to bootstrap the network but ensures they're on the same network
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
docker network connect "kind" "${reg_name}"
fi
# 5. Document the local registry
# https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${reg_port}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF

View File

@ -0,0 +1,16 @@
{
"Servers": {
"1": {
"Name": "futureporn",
"Group": "Servers",
"Host": "postgres.futureporn.svc.cluster.local",
"Port": 5432,
"MaintenanceDB": "postgres",
"Username": "postgres",
"UseSSHTunnel": 0,
"TunnelPort": "22",
"TunnelAuthentication": 0,
"KerberosAuthentication": false
}
}
}

View File

@ -0,0 +1,20 @@
# @todo this script is meant for first-time setup of the cluster
# it sets up the UI to have a futureporn postgres server connection
#
# @see https://www.pgadmin.org/docs/pgadmin4/development/import_export_servers.html#json-format
#
bindir=$(dirname "$(readlink -fm "$0")")
# kubectl -n futureporn exec pgadmin -- /usr/bin/python /pgadmin4/setup.py load-servers pgadmin-connection-profile.json
# kubectl -n futureporn exec pgadmin -- /usr/bin/python3 /pgadmin4/setup.py
kubectl -n futureporn cp ${bindir}/pgadmin-connection-profile.json pgadmin:/tmp/pgadmin-connection-profile.json
# kubectl -n futureporn exec pgadmin -- chown root:root /tmp/pgadmin-connection-profile.json
# kubectl -n futureporn exec pgadmin -- mv /tmp/pgadmin-connection-profile.json /pgadmin4/pgadmin-connection-profile.json
# kubectl -n futureporn cp pgadmin:/tmp/pgadmin-connection-profile.json pgadmin:/tmp/pgadmin-connection-profile.json
kubectl -n futureporn exec pgadmin -- ls -la /tmp/
kubectl -n futureporn exec pgadmin -- cat /tmp/pgadmin-connection-profile.json
kubectl -n futureporn exec pgadmin -- /venv/bin/python3 setup.py load-servers /tmp/pgadmin-connection-profile.json --replace --user cj@futureporn.net

3
scripts/postgres-drop.sh Normal file
View File

@ -0,0 +1,3 @@
## drop futureporn_db
kubectl -n futureporn exec postgres -- psql -U postgres --command "DROP DATABASE futureporn_db WITH (FORCE);"

View File

@ -0,0 +1,29 @@
## drop futureporn_db
kubectl -n futureporn exec postgres -- psql -U postgres --command "DROP DATABASE futureporn_db;"
## create futureporn_db
kubectl -n futureporn exec postgres -- psql -U postgres --command "\
CREATE DATABASE futureporn_db \
WITH \
OWNER = postgres \
ENCODING = 'UTF8' \
LOCALE_PROVIDER = 'libc' \
CONNECTION LIMIT = -1 \
IS_TEMPLATE = False;"
## restore
# kubectl -n futureporn exec postgres -- psql -U postgres -d futureporn_db -f - < "/home/cj/Documents/futureporn-meta/backups/2024-05-21_21-44-35-futureporn-db.psql"
# kubectl -n futureporn exec -it postgres -- bash -c "psql -U postgres futureporn_db -f /home/cj/Documents/futureporn-meta/backups/2024-05-21_21-44-35-futureporn-db.psql"
# kubectl -n futureporn exec -i postgres -- pg_restore -U postgres -d futureporn_db < /home/cj/Documents/futureporn-meta/backups/2024-05-21_21-44-35-futureporn-db.psql
# kubectl exec -i POD_NAME -- pg_restore -U USERNAME -C -d DATABASE < dump.sql
kubectl -n futureporn cp /home/cj/Documents/futureporn-meta/backups/2024-05-21_21-44-35-futureporn-db.psql postgres:/tmp/db.psql
kubectl -n futureporn exec -i postgres -- pg_restore -U postgres -d futureporn_db /tmp/db.psql
# kubectl -n futureporn exec -ti db-postgresql-0 -- rm /tmp/db.psql

58
scripts/postgres-seed.sh Normal file
View File

@ -0,0 +1,58 @@
#!/bin/sh
bindir=$(dirname "$(readlink -fm "$0")")
source "${bindir}/../.env"
if [ -z $POSTGRES_REALTIME_PASSWORD ]; then
echo "POSTGRES_REALTIME_PASSWORD was missing in env"
exit 5
fi
## Create the futureporn Strapi database
kubectl -n futureporn exec postgres -- psql -U postgres --command "\
CREATE DATABASE futureporn_db \
WITH \
OWNER = postgres \
ENCODING = 'UTF8' \
LOCALE_PROVIDER = 'libc' \
CONNECTION LIMIT = -1 \
IS_TEMPLATE = False;"
## Create the futureporn realtime database (for NOTIFY/AWAIT pubsub)
kubectl -n futureporn exec postgres -- psql -U postgres --command "\
CREATE DATABASE futureporn_realtime \
WITH \
OWNER = postgres \
ENCODING = 'UTF8' \
LOCALE_PROVIDER = 'libc' \
CONNECTION LIMIT = -1 \
IS_TEMPLATE = False;"
## create futureporn user
kubectl -n futureporn exec postgres -- psql -U postgres --command "\
CREATE ROLE futureporn \
WITH \
LOGIN \
NOSUPERUSER \
NOCREATEDB \
NOCREATEROLE \
INHERIT \
NOREPLICATION \
NOBYPASSRLS \
CONNECTION LIMIT -1 \
PASSWORD '$POSTGRES_REALTIME_PASSWORD';"
## grant futureporn user all privs
kubectl -n futureporn exec postgres -- psql -U postgres --command "\
GRANT ALL PRIVILEGES ON DATABASE futureporn_realtime TO futureporn;"
## import schema
## I have a file, schema.psql that I want to import. How do I do that?
# kubectl -n futureporn exec postgres -- psql -U postgres --command "\ ;"
kubectl -n futureporn exec postgres -- psql -U postgres -f - < "${bindir}/postgres-2024-05-09-futureporn_db-schema-only.psql"

View File

@ -1,34 +0,0 @@
FROM node:20-alpine as base
WORKDIR /app
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
RUN apk update && apk add --no-cache build-base gcc autoconf automake zlib-dev libpng-dev vips-dev libc6-compat git nasm bash gcompat
FROM base AS install
COPY ./packages/strapi/pnpm-lock.yaml ./packages/strapi/package.json ./
RUN pnpm install --prod --shamefully-hoist && pnpm run build
COPY ./packages/strapi .
RUN chown -R node:node /app
USER node
# FROM base AS install
# COPY ./packages/strapi/pnpm-lock.yaml ./
# RUN pnpm fetch --prod
# COPY ./packages/strapi .
# RUN pnpm i --offline --prod --shamefully-hoist && pnpm run build
# RUN chown -R node:node /app
# USER node
FROM install AS dev
ENV NODE_ENV=development
ENTRYPOINT ["pnpm"]
CMD ["run", "dev"]
FROM install AS release
ENV NODE_ENV=production
ENTRYPOINT ["pnpm"]
CMD ["run", "start"]

21
t.harness.tiltfile Normal file
View File

@ -0,0 +1,21 @@
# This starts ArgoCD in local kind cluster.
# Meant for validating cluster behavior before committing infrastructure to git
# We run ArgoCD in production so here we can test that everything works locally
load('ext://namespace', 'namespace_create')
k8s_namespace='fp'
namespace_create(k8s_namespace)
k8s_resource(
objects=[k8s_namespace + ':namespace'],
labels=["localdev"],
new_name='k8s:namespace'
)
k8s_context=k8s_context()
# Load ArgoCD Tiltfile
load('./localdev/argocd/Tiltfile', 'deploy_argo', 'delete_argocd_apps_on_tilt_down', 'force_argocd_cleanup_on_tilt_down')
# make sure apps get removed (cleanly) before ArgoCD is shutdown
delete_argocd_apps_on_tilt_down()
deploy_argo()

1
t.kustomize.tiltfile Normal file
View File

@ -0,0 +1 @@
kustomize ( './clusters/production' )

View File

@ -7,18 +7,42 @@ load('ext://dotenv', 'dotenv')
dotenv(fn='.env')
load('ext://helm_remote', 'helm_remote')
helm_remote(
'kubernetes-ingress-controller',
repo_name='kubernetes-ingress-controller',
repo_url='https://ngrok.github.io/kubernetes-ingress-controller',
namespace='futureporn',
create_namespace='false',
set=[
'credentials.apiKey=%s' % os.getenv('NGROK_API_KEY'),
'credentials.authtoken=%s' % os.getenv('NGROK_AUTHTOKEN')
]
)
# kubefwd all namespaces Tilt deploys to.
v1alpha1.extension_repo(name='default', url='https://github.com/tilt-dev/tilt-extensions')
v1alpha1.extension(name='kubefwd:config', repo_name='default', repo_path='kubefwd')
# v1alpha1.extension_repo(
# name='default',
# url='https://github.com/tilt-dev/tilt-extensions'
# )
# v1alpha1.extension(
# name='ngrok:config',
# repo_name='default',
# repo_path='ngrok',
# )
# args=['--default_config_file=%s' % os.getenv('TILT_NGROK_DEFAULT_CONFIG_FILE')]
# load('ext://helm_remote', 'helm_remote')
# helm_remote(
# 'frp-operator',
# repo_name='frp-operator',
# repo_url='https://zufardhiyaulhaq.com/frp-operator/charts/releases/',
# namespace='futureporn',
# version='1.0.0'
# )
# helm_remote(
# 'kubernetes-ingress-controller',
# repo_name='kubernetes-ingress-controller',
# repo_url='https://ngrok.github.io/kubernetes-ingress-controller',
# namespace='futureporn',
# create_namespace='false',
# set=[
# 'credentials.apiKey=%s' % os.getenv('NGROK_API_KEY'),
# 'credentials.authtoken=%s' % os.getenv('NGROK_AUTHTOKEN')
# ]
# )
k8s_yaml(helm(
'./charts/fp',
@ -27,15 +51,28 @@ k8s_yaml(helm(
# # docker_build('fp/link2cid', './packages/link2cid')
# docker_build('fp/link2cid', './packages/link2cid')
docker_build(
'fp/strapi',
'.',
build_args={
'NODE_ENV': 'development',
},
only=['./packages/strapi'],
dockerfile='./d.strapi.dockerfile',
live_update=[
sync('./packages/strapi', '/app')
]
)
# docker_build(
# 'fp/strapi',
# 'fp/strapi-app',
# '.',
# only=["./packages/strapi"],
# dockerfile='d.strapi.dockerfile',
# target='release',
# only=["./packages/strapi-app"],
# dockerfile='d.strapi-app.dockerfile',
# live_update=[
# sync('./packages/strapi', '/app')
# sync('./packages/strapi-app', '/app')
# ]
# )
@ -53,59 +90,25 @@ load('ext://uibutton', 'cmd_button')
# kubectl exec "$POD_NAME" -- $command
pg_seed_script = '''
kubectl -n futureporn exec postgres -- psql -U postgres --command "CREATE DATABASE futureporn_db WITH OWNER = postgres ENCODING = 'UTF8' LOCALE_PROVIDER = 'libc' CONNECTION LIMIT = -1 IS_TEMPLATE = False;"
'''
cmd_button('postgres:seed',
argv=['sh', '-c', pg_seed_script],
resource='postgres',
icon_name='dataset',
text='seed db with sample data',
argv=['sh', './scripts/postgres-seed.sh'],
resource='postgres',
icon_name='dataset',
text='seed db with schema',
)
cmd_button('postgres:restore',
argv=['sh', '-c', 'cd letters && yarn install'],
resource='postgres',
icon_name='cloud_download',
text='restore db from backup',
argv=['sh', './scripts/postgres-restore.sh'],
resource='postgres',
icon_name='cloud',
text='restore db from backup',
)
cmd_button('postgres:drop',
argv=['sh', './scripts/postgres-drop.sh'],
resource='postgres',
icon_name='delete',
text='delete the database'
)
pgadmin_import_script = '''
# @todo this script is meant for first-time setup of the cluster
# it sets up the UI to have a futureporn postgres server connection
#
# @see https://www.pgadmin.org/docs/pgadmin4/development/import_export_servers.html#json-format
#
# futureporn.pgadmin.json
{
"Servers": {
"1": {
"Name": "futureporn",
"Group": "Servers",
"Host": "postgres.futureporn.svc.cluster.local",
"Port": 5432,
"MaintenanceDB": "postgres",
"Username": "postgres",
"UseSSHTunnel": 0,
"TunnelPort": "22",
"TunnelAuthentication": 0,
"KerberosAuthentication": false,
"ConnectionParameters": {
"sslmode": "prefer",
"connect_timeout": 10,
"sslcert": "<STORAGE_DIR>/.postgresql/postgresql.crt",
"sslkey": "<STORAGE_DIR>/.postgresql/postgresql.key"
}
}
}
}
kubectl -n futureporn exec pgadmin -- /usr/bin/python /pgadmin/setup.py load-servers futureporn.pgadmin.json
'''
cmd_button('pgadmin:server',
argv=['sh', '-c', pgadmin_import_script],
resource='pgadmin',
icon_name='dataset',
text='create connection'
)
## Uncomment the following for fp/next in dev mode
@ -117,7 +120,7 @@ docker_build(
dockerfile='d.next.dockerfile',
target='dev',
build_args={
'NEXT_PUBLIC_STRAPI_URL': 'http://strapi.futureporn.svc.cluster.local:1337'
'NEXT_PUBLIC_STRAPI_URL': 'http://strapi.futureporn.svc.cluster.local:1339'
},
live_update=[
sync('./packages/next', '/app')
@ -128,23 +131,36 @@ docker_build(
docker_build(
'fp/scout',
'.',
only=['./pnpm-lock.yaml', './package.json', './packages/scout'],
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/pg-pubsub'],
dockerfile='d.scout.dockerfile',
target='scout',
live_update=[
sync('./packages/scout', '/app')
]
)
k8s_resource(
workload='kubernetes-ingress-controller-manager',
links=[
link(os.getenv('NGROK_URL'), 'Endpoint')
],
labels='ngrok'
)
# k8s_resource(
# workload='kubernetes-ingress-controller-manager',
# links=[
# link(os.getenv('NGROK_URL'), 'Endpoint')
# ],
# labels='ngrok'
# )
# k8s_resource(
# workload='frp-operator-controller-manager',
# labels='tunnel'
# )
# k8s_resource(
# workload='echo-deployment',
# port_forwards=['8080'],
# links=[
# link('https://echo.piko.sbtp.xyz'),
# link('http://echo.futureporn.svc.cluster.local:8080')
# ],
# labels='debug'
# )
# k8s_resource(
# workload='snake',
# port_forwards=['8080'],
# labels='debug'
# )
# k8s_resource(
@ -155,18 +171,37 @@ k8s_resource(
k8s_resource(
workload='next',
port_forwards=['3000']
port_forwards=['3000'],
links=[
link('http://next.futureporn.svc.cluster.local:3000'),
link('https://next.piko.sbtp.xyz'),
]
)
k8s_resource(
workload='strapi',
port_forwards=['1337'],
port_forwards=['1339'],
links=[
link('http://localhost:1337/admin', 'Strapi Admin UI')
link('http://localhost:1339/admin'),
link('http://strapi.futureporn.svc.cluster.local:1339'),
link('https://strapi.piko.sbtp.xyz'),
]
)
# k8s_resource(
# workload='strapi-app',
# port_forwards=['1338']
# )
k8s_resource(
workload='postgres',
)
k8s_resource(
workload='scout',
port_forwards=['5000']
)
k8s_resource(
workload='postgres',
port_forwards=['5432']
)
k8s_resource(
workload='pgadmin',
port_forwards=['5050']