Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abd3daea7e | |||
| 0da6dc7810 | |||
| d8d152e249 | |||
| 75ea9b7416 | |||
| cd36fc5723 | |||
| 3987a4e43e | |||
| 5ca66f3ed7 |
+1
-1
@@ -1,5 +1,5 @@
|
||||
# SQLite database file (mounted on a persistent volume in Docker)
|
||||
DATABASE_URL="file:/app/data/askedout.db"
|
||||
DATABASE_URL="file:/app/data/lovelope.db"
|
||||
|
||||
# Public base URL (used in QR codes and share links)
|
||||
APP_URL="http://localhost:3000"
|
||||
|
||||
@@ -38,8 +38,5 @@ yarn-error.log*
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# prisma
|
||||
prisma/migrations/*/migration.sql
|
||||
|
||||
# local Claude Code tooling (skills, agent config) - not part of the app
|
||||
/.claude
|
||||
|
||||
+4
-4
@@ -28,7 +28,7 @@ RUN npm run build
|
||||
|
||||
# ── Stage 3: runner ────────────────────────────────────────────────────────────
|
||||
FROM node:20-alpine AS runner
|
||||
RUN apk add --no-cache libc6-compat openssl
|
||||
RUN apk add --no-cache libc6-compat openssl su-exec
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
@@ -48,11 +48,11 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
RUN mkdir -p /app/data && chown nextjs:nodejs /app/data
|
||||
|
||||
USER nextjs
|
||||
COPY --chmod=755 docker-entrypoint.sh ./docker-entrypoint.sh
|
||||
|
||||
EXPOSE 3000
|
||||
ENV PORT=3000
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
|
||||
# Run migrations then start the server
|
||||
CMD ["sh", "-c", "npx prisma migrate deploy && node server.js"]
|
||||
# Fix ownership of the bind-mounted /app/data volume, then drop to nextjs
|
||||
ENTRYPOINT ["./docker-entrypoint.sh"]
|
||||
|
||||
+57
-12
@@ -46,23 +46,68 @@ Types: `feat`, `fix`, `refactor`, `style`, `docs`, `chore`, `test`.
|
||||
- Never force-push `main`. Force-pushing a feature branch you own (after a
|
||||
rebase) is fine; never force-push a branch someone else might be using.
|
||||
|
||||
## Tags / releases
|
||||
## Releases & Docker images
|
||||
|
||||
Tag production releases as `v<major>.<minor>.<patch>` (semver) once this
|
||||
project has a deployment cadence worth marking. Not required for every merge.
|
||||
CI (`.gitea/workflows/docker-publish.yml`) never builds an image from a
|
||||
plain commit or merge to `main`/`master` — only from two deliberate
|
||||
triggers, each producing tags on both the Gitea registry and GHCR:
|
||||
|
||||
## Docker images
|
||||
| Trigger | Produces | Purpose |
|
||||
|------------------|--------------------------|-------------------------------------|
|
||||
| push to `dev` | `:dev` | test a build before releasing |
|
||||
| push a `v*` tag | `:latest` + `:vX.Y.Z` | ship an actual release |
|
||||
|
||||
CI (`.gitea/workflows/docker-publish.yml`) only builds an image on two
|
||||
events, not on every commit:
|
||||
### Testing a build (`dev`)
|
||||
|
||||
- push to `dev` — builds a rolling `:dev` tag, for testing in-progress work
|
||||
before it's ready to release.
|
||||
- push a `v*` tag — builds `:latest` plus the version tag, for actual
|
||||
releases.
|
||||
`dev` is a disposable pointer, not a branch with history of its own — it
|
||||
should always match `main`/`master` exactly. Bring it up to date and push
|
||||
whenever you want a fresh test image:
|
||||
|
||||
Push to `dev` when you want a throwaway build to test; tag a release on
|
||||
`main`/`master` when you want a real one.
|
||||
```
|
||||
git checkout dev
|
||||
git merge main --ff-only
|
||||
git push origin dev
|
||||
```
|
||||
|
||||
If `dev` has drifted and won't fast-forward (e.g. after a rebase, or a
|
||||
commit landed only on `dev` by mistake), reset it instead of merging —
|
||||
`dev` has no unique history worth preserving:
|
||||
|
||||
```
|
||||
git checkout dev
|
||||
git reset --hard main
|
||||
git push origin dev --force-with-lease
|
||||
```
|
||||
|
||||
### Shipping a release (tag)
|
||||
|
||||
1. Bump the version in `package.json` on `main`/`master` (a plain commit —
|
||||
builds nothing on its own):
|
||||
```
|
||||
# edit package.json: "version": "X.Y.Z"
|
||||
git add package.json
|
||||
git commit -m "chore: bump version to X.Y.Z"
|
||||
git push origin main
|
||||
```
|
||||
2. Tag that commit and push the tag — this is what actually triggers the
|
||||
release build:
|
||||
```
|
||||
git tag vX.Y.Z
|
||||
git push origin vX.Y.Z
|
||||
```
|
||||
3. Never move or re-push an existing tag once it's out (it may already be
|
||||
pulled/deployed). If you need to fix something after tagging, bump to
|
||||
the next patch version and tag again instead.
|
||||
|
||||
Use `v<major>.<minor>.<patch>` (semver) for tags.
|
||||
|
||||
### Required repo secrets/variables (Gitea → Settings → Actions)
|
||||
|
||||
- `vars.REGISTRY_HOST` — the Gitea instance hostname (no protocol), used to
|
||||
tag/push to the built-in Gitea container registry.
|
||||
- `secrets.REGISTRY_TOKEN` — a Gitea PAT with `write:package` scope.
|
||||
- `secrets.GHCR_TOKEN` — a GitHub PAT with `write:packages` scope, used to
|
||||
also push to `ghcr.io`.
|
||||
|
||||
## What not to commit
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
DATABASE_URL: "file:/app/data/askedout.db"
|
||||
DATABASE_URL: "file:/app/data/lovelope.db"
|
||||
APP_URL: "http://localhost:3000"
|
||||
GIPHY_API_KEY: "${GIPHY_API_KEY:-}"
|
||||
volumes:
|
||||
@@ -35,11 +35,11 @@ Then:
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Open **http://localhost:3000**. Your data lives in `./data/askedout.db` on your machine.
|
||||
Open **http://localhost:3000**. Your data lives in `./data/lovelope.db` on your machine.
|
||||
|
||||
Want GIF search to work? Grab a free key at [developers.giphy.com](https://developers.giphy.com/), export it as `GIPHY_API_KEY` before running `docker compose up` (or drop it in a `.env` file next to `docker-compose.yml`), then restart. Without it, the app still runs fine, GIF search is just disabled.
|
||||
|
||||
Pin a specific version instead of always tracking `latest` by using a release tag, e.g. `ghcr.io/jeangaston/lovelope:v0.1.0`.
|
||||
Pin a specific version instead of always tracking `latest` by using a release tag, e.g. `ghcr.io/jeangaston/lovelope:v1.0.0`.
|
||||
|
||||
### Build from source
|
||||
|
||||
@@ -83,7 +83,7 @@ Browsers never send the part after `#` to any server, so that key never reaches
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `DATABASE_URL` | ✅ | SQLite file path, default `file:/app/data/askedout.db` works out of the box with Docker |
|
||||
| `DATABASE_URL` | ✅ | SQLite file path, default `file:/app/data/lovelope.db` works out of the box with Docker |
|
||||
| `APP_URL` | ✅ | Base URL used to build share links, e.g. `http://localhost:3000` |
|
||||
| `GIPHY_API_KEY` | optional | Enables GIF search. Free key at [developers.giphy.com](https://developers.giphy.com/), leave blank to disable |
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ services:
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
DATABASE_URL: "file:/app/data/askedout.db"
|
||||
DATABASE_URL: "file:/app/data/lovelope.db"
|
||||
APP_URL: "http://localhost:3000"
|
||||
NODE_ENV: "development"
|
||||
GIPHY_API_KEY: "${GIPHY_API_KEY:-}"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
chown -R nextjs:nodejs /app/data
|
||||
|
||||
exec su-exec nextjs sh -c "npx prisma migrate deploy && node server.js"
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lovelope",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.3",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Proposal" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"slug" TEXT NOT NULL,
|
||||
"manageToken" TEXT NOT NULL,
|
||||
"senderName" TEXT NOT NULL,
|
||||
"recipientName" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"message" TEXT NOT NULL,
|
||||
"theme" TEXT NOT NULL DEFAULT 'sunset',
|
||||
"gradientFrom" TEXT,
|
||||
"gradientVia" TEXT,
|
||||
"gradientTo" TEXT,
|
||||
"gifUrl" TEXT,
|
||||
"evasiveNo" BOOLEAN NOT NULL DEFAULT false,
|
||||
"status" TEXT NOT NULL DEFAULT 'draft',
|
||||
"expiresAt" DATETIME,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ActivityOption" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"proposalId" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"emoji" TEXT NOT NULL DEFAULT '🎉',
|
||||
"order" INTEGER NOT NULL DEFAULT 0,
|
||||
CONSTRAINT "ActivityOption_proposalId_fkey" FOREIGN KEY ("proposalId") REFERENCES "Proposal" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TimeSlot" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"activityOptionId" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"startsAt" TEXT,
|
||||
CONSTRAINT "TimeSlot_activityOptionId_fkey" FOREIGN KEY ("activityOptionId") REFERENCES "ActivityOption" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Response" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"proposalId" TEXT NOT NULL,
|
||||
"selectedActivityId" TEXT,
|
||||
"selectedTimeSlotId" TEXT,
|
||||
"answer" TEXT NOT NULL,
|
||||
"note" TEXT,
|
||||
"respondedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "Response_proposalId_fkey" FOREIGN KEY ("proposalId") REFERENCES "Proposal" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "Response_selectedActivityId_fkey" FOREIGN KEY ("selectedActivityId") REFERENCES "ActivityOption" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
|
||||
CONSTRAINT "Response_selectedTimeSlotId_fkey" FOREIGN KEY ("selectedTimeSlotId") REFERENCES "TimeSlot" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Proposal_slug_key" ON "Proposal"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Proposal_manageToken_key" ON "Proposal"("manageToken");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Response_proposalId_key" ON "Response"("proposalId");
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "ActivityOption" ADD COLUMN "location" TEXT;
|
||||
Reference in New Issue
Block a user