Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ DATABASE_URL=postgresql://latex:latex@db:5432/latex
STORAGE_BACKEND=local
ADMIN_BOOTSTRAP_TOKEN="set-a-random-one-time-bootstrap-token"
RUN_DB_MIGRATIONS_ON_STARTUP=false
DB_PUSH_PW="only set this if you want to use the /api/admin/settings/db-push endpoint"
DB_PUSH_PW="only set this if you want to use the /api/admin/settings/db-push endpoint"
RESEND_API_KEY=
PASSWORD_RESET_TOKEN_TTL_MINUTES=30
4 changes: 2 additions & 2 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm lint
#- name: Lint
# run: pnpm lint

- name: Build
run: pnpm build
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint
run: pnpm lint
#- name: Lint
# run: pnpm lint

- name: Build
run: pnpm build
Expand Down
8 changes: 6 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ ENV NODE_ENV=production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED=1

# Needed for admin migration import route (`psql` / `pg_restore`).
RUN apk add --no-cache postgresql-client
# Needed for admin migration import route (`psql` / `pg_restore`),
# document preview generation (`pdftoppm` + `soffice`),
# document/text preview rendering (`fontconfig` + actual fonts),
# and video preview frame extraction (`ffmpeg`).
RUN apk add --no-cache postgresql-client poppler-utils libreoffice fontconfig ttf-dejavu ffmpeg \
&& fc-cache -f

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
Expand Down
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ To run the app directly without docker, correctly set your

## TODO:

- [ ] alternative 'tiles' layout for 'guest' album view, button to switch between that and current fullwidth layout, and a slider to adjust tile size


### TODO LATERER:

- [ ] TUI

### DONE:

- [x] give album view the same controls / layout as gallery view
- [x] next / prev buttons when viewing image in modal on a gallery / album
- [x] support .gifs
Expand All @@ -111,11 +120,9 @@ To run the app directly without docker, correctly set your
- [x] ability to rename albums (just click on the title and start typing)
- [x] ability to caption images in an album (caption applies to the image in the context of the image-in-that-album, not to the image, as images can be in more than one album)
- [x] add landon's [ditherspace](https://landonjsmith.com/projects/ditherspace.html) to this app
- [x] encrypted S3 storage
- [x] keyboard shortcuts for img view
- [ ] alternative 'tiles' layout for 'guest' album view, button to switch between that and current fullwidth layout, and a slider to adjust tile size

### TODO LATERER:

- [ ] TUI
- [ ] support video formats
- [ ] support any file format
- [x] password reset
- [x] support video formats
- [x] support any file format (documents, archives)
- [x] generate previews for documents
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ services:
- RATE_LIMIT_MAX_ATTEMPTS=${RATE_LIMIT_MAX_ATTEMPTS:-20}
- BILLING_ROLE_ARN=${BILLING_ROLE_ARN}
- BILLING_CE_REGION=${BILLING_CE_REGION:-us-east-1}
- RESEND_API_KEY=${RESEND_API_KEY}
- PASSWORD_RESET_TOKEN_TTL_MINUTES=${PASSWORD_RESET_TOKEN_TTL_MINUTES}
restart: unless-stopped
depends_on:
db:
Expand Down
2 changes: 2 additions & 0 deletions docs/runtime-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The app supports both local and AWS runtime settings.

- `NEXTAUTH_URL`
- `NEXTAUTH_SECRET`
- `PASSWORD_RESET_TOKEN_TTL_MINUTES` (optional, defaults to `30`)
- `RESEND_API_KEY` (required for password reset email delivery)
- `ADMIN_BOOTSTRAP_TOKEN` (required only for one-time `/promote-admin` bootstrap)
- `STORAGE_BACKEND` (`local` or `s3`)
- `RUN_DB_MIGRATIONS_ON_STARTUP` (`true` only for local/dev smoke use)
Expand Down
75 changes: 58 additions & 17 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,60 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
import { defineConfig, globalIgnores } from 'eslint/config';
import nextVitals from 'eslint-config-next/core-web-vitals'
import tseslint from 'typescript-eslint';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
baseDirectory: __dirname,
});

const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
export default defineConfig([
...nextVitals,
tseslint.configs.recommendedTypeChecked,
{
ignores: ["src/generated/**/*"]
}
];

export default eslintConfig;
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
parserOptions: {
projectService: true,
},
},
},
{
settings: {
react: { version: "19" } // Avoids auto-detection crash
},
rules: {
'import/no-dynamic-require': 'warn',
'import/no-unused-modules': 'warn',
"@typescript-eslint/no-unused-vars": [
"warn",
{
args: "all",
argsIgnorePattern: "^_",
caughtErrors: "all",
caughtErrorsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
varsIgnorePattern: "^_",
ignoreRestSiblings: true
}
],
"@typescript-eslint/no-confusing-void-expression": [
"error",
{
ignoreArrowShorthand: true
}
],
"@typescript-eslint/no-misused-promises": [
"error",
{
checksVoidReturn: {
attributes: false
}
}
]
},
},
globalIgnores([
'.next/**',
'out/**',
'build/**',
'next-env.d.ts',
"**/*.js", "**/*.cjs", "**/*.mjs",
'*.config.ts'
]),
]);
2 changes: 2 additions & 0 deletions infra/cdk/lib/app-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,15 @@ export class AppStack extends cdk.Stack {
RATE_LIMIT_MAX_ATTEMPTS: "20",
BILLING_ROLE_ARN: billingReaderRole.roleArn,
BILLING_CE_REGION: "us-east-1",
PASSWORD_RESET_TOKEN_TTL_MINUTES: props.config.passwordResetTokenTtlMinutes.toString(),
},
secrets: {
NEXTAUTH_SECRET: ecs.Secret.fromSecretsManager(props.appSecret, "NEXTAUTH_SECRET"),
ADMIN_BOOTSTRAP_TOKEN: ecs.Secret.fromSecretsManager(props.appSecret, "ADMIN_BOOTSTRAP_TOKEN"),
DB_PUSH_PW: ecs.Secret.fromSecretsManager(props.appSecret, "DB_PUSH_PW"),
PGUSER: ecs.Secret.fromSecretsManager(props.dbCredentialsSecret, "username"),
PGPASSWORD: ecs.Secret.fromSecretsManager(props.dbCredentialsSecret, "password"),
RESEND_API_KEY: ecs.Secret.fromSecretsManager(props.appSecret, "RESEND_API_KEY"),
},
readonlyRootFilesystem: true,
});
Expand Down
3 changes: 2 additions & 1 deletion infra/cdk/lib/cicd-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class CiCdStack extends cdk.Stack {
"ssm:*",
"sts:GetCallerIdentity",
],
// TODO: Restrict to only the necessary resources
resources: ["*"],
}),
new iam.PolicyStatement({
Expand All @@ -61,7 +62,7 @@ export class CiCdStack extends cdk.Stack {
this.createRole("DevDeployRole", {
provider,
policy: basePolicy,
githubSub: `repo:${props.githubOrg}/${props.githubRepo}:ref:refs/heads/main`,
githubSub: `repo:${props.githubOrg}/${props.githubRepo}:ref:refs/heads/*`,
roleName: `${props.config.appName}-github-deploy-dev`,
});

Expand Down
2 changes: 2 additions & 0 deletions infra/cdk/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ export type EnvironmentConfig = {
s3UseKmsEncryption: boolean;
s3Versioned: boolean;
s3NoncurrentVersionExpirationDays: number;
passwordResetTokenTtlMinutes: number;
};

const BASE = {
appName: "latex",
region: "ap-southeast-2",
passwordResetTokenTtlMinutes: 30,
} as const;

const CONFIG_BY_ENV: Record<EnvironmentName, Omit<EnvironmentConfig, "environment">> = {
Expand Down
18 changes: 18 additions & 0 deletions infra/cdk/lib/data-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,24 @@ export class DataStack extends cdk.Stack {
lifecycleRules: imageBucketLifecycleRules,
removalPolicy: cdk.RemovalPolicy.RETAIN,
autoDeleteObjects: false,
cors: [
{
allowedMethods: [s3.HttpMethods.GET, s3.HttpMethods.HEAD],
allowedOrigins: ["*"],
allowedHeaders: ["*"],
exposedHeaders: [
"Accept-Ranges",
"Content-Length",
"Content-Range",
"Content-Type",
"ETag",
"Last-Modified",
"x-amz-request-id",
"x-amz-id-2",
],
maxAge: 3600,
},
],
});

const dbSecurityGroup = new ec2.SecurityGroup(this, "DbSecurityGroup", {
Expand Down
17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next build && next dev --turbopack -p 3000",
"dev-quick": "next dev --turbopack -p 3000",
"devbuild": "next build && next dev --turbopack -p 3000",
"dev": "next dev --turbopack -p 3000",
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint:es": "pnpm exec eslint",
"db:push": "drizzle-kit push --config ./drizzle.config.ts",
"db:push:force": "drizzle-kit push --force --config ./drizzle.config.ts",
"cdk:install": "pnpm --dir infra/cdk install",
"cdk:bootstrap:dev": "./scripts/infra/bootstrap-cdk.sh dev",
"cdk:bootstrap:prod": "./scripts/infra/bootstrap-cdk.sh prod",
"infra:image:push:dev": "./scripts/infra/build-and-push-image.sh dev",
"infra:image:push:prod": "./scripts/infra/build-and-push-image.sh prod",
"infra:deploy:dev:app": "bash ./scripts/infra/deploy-app.sh dev",
"infra:deploy:prod:app": "bash ./scripts/infra/deploy-app.sh prod"
"infra:deploy:dev": "bash ./scripts/infra/deploy-app.sh dev",
"infra:deploy:prod": "bash ./scripts/infra/deploy-app.sh prod"
},
"dependencies": {
"@aws-sdk/client-cost-explorer": "^3.996.0",
Expand All @@ -25,6 +26,7 @@
"@aws-sdk/client-secrets-manager": "^3.954.0",
"@aws-sdk/client-ssm": "^3.968.0",
"@aws-sdk/credential-providers": "^3.969.0",
"@aws-sdk/s3-request-presigner": "^3.986.0",
"@energiz3r/icon-library": "^0.0.6",
"@hookform/resolvers": "^5.2.2",
"@monaco-editor/react": "^4.7.0",
Expand Down Expand Up @@ -53,6 +55,7 @@
"drizzle-kit": "^0.31.8",
"drizzle-orm": "^0.45.1",
"lucide-react": "^0.544.0",
"mammoth": "^1.11.0",
"next": "^15.5.10",
"next-auth": "^4.24.11",
"postgres": "^3.4.7",
Expand All @@ -62,6 +65,7 @@
"react-hook-form": "^7.64.0",
"react-markdown": "^10.1.0",
"remark": "^15.0.1",
"resend": "^6.9.3",
"sharp": "^0.33.5",
"strip-markdown": "^6.0.0",
"stripe": "^19.3.1",
Expand All @@ -77,10 +81,11 @@
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"eslint": "^9.37.0",
"eslint-config-next": "15.5.4",
"eslint-config-next": "^16.1.6",
"prettier": "^3.8.0",
"tailwindcss": "^4.1.14",
"typescript": "^5.9.3"
"typescript": "^5.9.3",
"typescript-eslint": "^8.56.1"
},
"packageManager": "pnpm@10.16.1+sha512.0e155aa2629db8672b49e8475da6226aa4bdea85fdcdfdc15350874946d4f3c91faaf64cbdc4a5d1ab8002f473d5c3fcedcd197989cf0390f9badd3c04678706"
}
Loading