diff --git a/README.md b/README.md
index 12645122..fff0f43d 100644
--- a/README.md
+++ b/README.md
@@ -158,8 +158,7 @@ Application behavior can be changed by configuring the following environment var
#### Settings
- `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information)
- `NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1` enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage)
-- `NEXT_PUBLIC_PUBLIC_FEED = 1` enables public feed available at `/feed.json` and `/rss.xml`
-- `NEXT_PUBLIC_PUBLIC_API = 1` enables public API available at `/api`
+- `NEXT_PUBLIC_SITE_FEEDS = 1` enables feeds at `/feed.json` and `/rss.xml`
- `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order
- `NEXT_PUBLIC_OG_TEXT_ALIGNMENT = BOTTOM` keeps OG image text bottom aligned (default is top)
diff --git a/app/api/route.ts b/app/api/route.ts
deleted file mode 100644
index fe566c70..00000000
--- a/app/api/route.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { getPhotosCached } from '@/photo/cache';
-import { API_PHOTO_REQUEST_LIMIT, formatPhotoForApi } from '@/app/api';
-import {
- BASE_URL,
- PUBLIC_API_ENABLED,
- META_TITLE,
-} from '@/app/config';
-
-export const dynamic = 'force-dynamic';
-
-export async function GET() {
- if (PUBLIC_API_ENABLED) {
- const photos = await getPhotosCached({ limit: API_PHOTO_REQUEST_LIMIT });
- return Response.json({
- meta: {
- title: META_TITLE,
- url: BASE_URL,
- },
- photos: photos.map(formatPhotoForApi),
- });
- } else {
- return new Response('API access disabled', { status: 404 });
- }
-}
diff --git a/app/feed.json/route.ts b/app/feed.json/route.ts
index 3422017e..3078cdfd 100644
--- a/app/feed.json/route.ts
+++ b/app/feed.json/route.ts
@@ -1,18 +1,17 @@
import { getPhotosCached } from '@/photo/cache';
-import { INFINITE_SCROLL_FEED_INITIAL } from '@/photo';
import {
BASE_URL,
- PUBLIC_FEED_ENABLED,
+ SITE_FEEDS_ENABLED,
META_TITLE,
} from '@/app/config';
-import { formatPhotoForFeedJson } from '@/app/feed';
+import { FEED_PHOTO_REQUEST_LIMIT, formatPhotoForFeedJson } from '@/app/feed';
export const dynamic = 'force-static';
export async function GET() {
- if (PUBLIC_FEED_ENABLED) {
+ if (SITE_FEEDS_ENABLED) {
const photos = await getPhotosCached({
- limit: INFINITE_SCROLL_FEED_INITIAL,
+ limit: FEED_PHOTO_REQUEST_LIMIT,
sortBy: 'createdAt',
});
return Response.json({
diff --git a/app/layout.tsx b/app/layout.tsx
index dc8a19ae..3eeaa1e2 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -10,7 +10,7 @@ import {
META_TITLE,
HTML_LANG,
NAV_CAPTION,
- PUBLIC_FEED_ENABLED,
+ SITE_FEEDS_ENABLED,
} from '@/app/config';
import AppStateProvider from '@/state/AppStateProvider';
import ToasterWithThemes from '@/toast/ToasterWithThemes';
@@ -66,7 +66,7 @@ export const metadata: Metadata = {
type: 'image/png',
sizes: '180x180',
}],
- ...PUBLIC_FEED_ENABLED && {
+ ...SITE_FEEDS_ENABLED && {
alternates: {
types: {
'application/rss+xml': '/rss.xml',
diff --git a/app/rss.xml/route.ts b/app/rss.xml/route.ts
index 3499818a..79573fca 100644
--- a/app/rss.xml/route.ts
+++ b/app/rss.xml/route.ts
@@ -4,14 +4,15 @@ import {
BASE_URL,
META_DESCRIPTION,
META_TITLE,
- PUBLIC_FEED_ENABLED,
+ SITE_FEEDS_ENABLED,
} from '@/app/config';
import { feedPhotoToXml, formatPhotoForFeedRss } from '@/app/feed';
+import { ABSOLUTE_PATH_FOR_FEED_JSON } from '@/app/paths';
export const dynamic = 'force-static';
export async function GET() {
- if (PUBLIC_FEED_ENABLED) {
+ if (SITE_FEEDS_ENABLED) {
const photos = await getPhotosCached({
limit: INFINITE_SCROLL_FEED_INITIAL,
sortBy: 'createdAt',
@@ -26,13 +27,11 @@ export async function GET() {
${META_TITLE}
-
${BASE_URL}
${META_DESCRIPTION}
-
- ${items.join('\n\n ')}
-
+ ${items.join('\n')}
`,
diff --git a/src/admin/AdminAppConfigurationClient.tsx b/src/admin/AdminAppConfigurationClient.tsx
index 10b9fffe..9e5fd412 100644
--- a/src/admin/AdminAppConfigurationClient.tsx
+++ b/src/admin/AdminAppConfigurationClient.tsx
@@ -31,6 +31,8 @@ import ScoreCardContainer from '@/components/ScoreCardContainer';
import { DEFAULT_CATEGORY_KEYS, getHiddenCategories } from '@/category';
import { AI_AUTO_GENERATED_FIELDS_ALL } from '@/photo/ai';
import clsx from 'clsx/lite';
+import Link from 'next/link';
+import { PATH_FEED_JSON, PATH_RSS_XML } from '@/app/paths';
export default function AdminAppConfigurationClient({
// Storage
@@ -103,8 +105,7 @@ export default function AdminAppConfigurationClient({
// Settings
isGeoPrivacyEnabled,
arePublicDownloadsEnabled,
- isPublicApiEnabled,
- isPublicFeedEnabled,
+ areSiteFeedsEnabled,
isPriorityOrderEnabled,
isOgTextBottomAligned,
// Internal
@@ -178,6 +179,15 @@ export default function AdminAppConfigurationClient({
{message}
;
+ const renderLink = (href: string, children?: ReactNode) =>
+
+ {children || href}
+ ;
+
return (
- Set environment variable to {'"1"'} to enable
- a public feed available at /feed.json
- and /rss.xml:
- {renderEnvVars(['NEXT_PUBLIC_PUBLIC_FEED'])}
-
-
- Set environment variable to {'"1"'} to enable
- a public API available at /api:
- {renderEnvVars(['NEXT_PUBLIC_PUBLIC_API'])}
+ Set environment variable to {'"1"'} to enable feeds at
+ {' '}
+ {renderLink(PATH_FEED_JSON)} and {renderLink(PATH_RSS_XML)}:
+ {renderEnvVars(['NEXT_PUBLIC_SITE_FEEDS'])}
-}
-
-export const formatPhotoForApi = (photo: Photo): PublicApiPhoto => ({
- id: photo.id,
- title: photo.title,
- url: absolutePathForPhoto({ photo }),
- ...photo.make && { make: photo.make },
- ...photo.model && { model: photo.model },
- ...photo.tags.length > 0 && { tags: photo.tags },
- takenAtNaive: formatDateFromPostgresString(photo.takenAtNaive),
- src: {
- small: getNextImageUrlForRequest({ imageUrl: photo.url, size: 200 }),
- medium: getNextImageUrlForRequest({ imageUrl: photo.url, size: 640 }),
- large: getNextImageUrlForRequest({ imageUrl: photo.url, size: 1200 }),
- },
-});
diff --git a/src/app/config.ts b/src/app/config.ts
index 40f7b97e..fb7c6b2a 100644
--- a/src/app/config.ts
+++ b/src/app/config.ts
@@ -307,10 +307,8 @@ export const GEO_PRIVACY_ENABLED =
process.env.NEXT_PUBLIC_GEO_PRIVACY === '1';
export const ALLOW_PUBLIC_DOWNLOADS =
process.env.NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS === '1';
-export const PUBLIC_FEED_ENABLED =
- process.env.NEXT_PUBLIC_PUBLIC_FEED === '1';
-export const PUBLIC_API_ENABLED =
- process.env.NEXT_PUBLIC_PUBLIC_API === '1';
+export const SITE_FEEDS_ENABLED =
+ process.env.NEXT_PUBLIC_SITE_FEEDS === '1';
export const PRIORITY_ORDER_ENABLED =
process.env.NEXT_PUBLIC_IGNORE_PRIORITY_ORDER !== '1';
export const OG_TEXT_BOTTOM_ALIGNMENT =
@@ -419,8 +417,7 @@ export const APP_CONFIGURATION = {
// Settings
isGeoPrivacyEnabled: GEO_PRIVACY_ENABLED,
arePublicDownloadsEnabled: ALLOW_PUBLIC_DOWNLOADS,
- isPublicApiEnabled: PUBLIC_API_ENABLED,
- isPublicFeedEnabled: PUBLIC_FEED_ENABLED,
+ areSiteFeedsEnabled: SITE_FEEDS_ENABLED,
isPriorityOrderEnabled: PRIORITY_ORDER_ENABLED,
isOgTextBottomAligned: OG_TEXT_BOTTOM_ALIGNMENT,
// Internal
diff --git a/src/app/feed.ts b/src/app/feed.ts
index 7d4dc159..12d4eab5 100644
--- a/src/app/feed.ts
+++ b/src/app/feed.ts
@@ -6,7 +6,7 @@ import {
} from '@/platforms/next-image';
import { formatDate, formatDateFromPostgresString } from '@/utility/date';
-export const API_PHOTO_REQUEST_LIMIT = 40;
+export const FEED_PHOTO_REQUEST_LIMIT = 40;
export const FEED_PHOTO_WIDTH_SMALL = 200;
export const FEED_PHOTO_WIDTH_MEDIUM = 640;
diff --git a/src/app/paths.ts b/src/app/paths.ts
index 5a06f14d..3829471c 100644
--- a/src/app/paths.ts
+++ b/src/app/paths.ts
@@ -15,6 +15,10 @@ export const PATH_API = '/api';
export const PATH_SIGN_IN = '/sign-in';
export const PATH_OG = '/og';
+// Feeds
+export const PATH_FEED_JSON = '/feed.json';
+export const PATH_RSS_XML = '/rss.xml';
+
export const PATH_GRID_INFERRED = GRID_HOMEPAGE_ENABLED
? PATH_ROOT
: PATH_GRID;
@@ -167,6 +171,12 @@ export const pathForRecipe = (recipe: string) =>
`${PREFIX_RECIPE}/${recipe}`;
// Absolute paths
+export const ABSOLUTE_PATH_FOR_FEED_JSON =
+ `${getBaseUrl()}${PATH_FEED_JSON}`;
+
+export const ABSOLUTE_PATH_FOR_RSS_XML =
+ `${getBaseUrl()}${PATH_RSS_XML}`;
+
export const ABSOLUTE_PATH_FOR_HOME_IMAGE =
`${getBaseUrl()}/home-image`;