Compare commits

...
Sign in to create a new pull request.

26 commits

Author SHA1 Message Date
SleeplessOne1917
b6415f828e
Merge pull request #1711 from LemmyNet/cache-dev
Fix some issues
2023-06-29 19:26:40 -04:00
SleeplessOne1917
cc184a86c8 Fix authorized route false flag 2023-06-29 18:12:22 -04:00
SleeplessOne1917
2d88e42cab Fix dev caching issue 2023-06-29 16:33:08 -04:00
Dessalines
9e7fec772d v0.18.1-rc.5 2023-06-29 16:20:38 -04:00
SleeplessOne1917
df39e0fe5d
Merge pull request #1708 from LemmyNet/cache-control
Cache static data for a day
2023-06-29 14:19:07 -04:00
SleeplessOne1917
fa41117320
Merge branch 'main' into cache-control 2023-06-29 13:35:44 -04:00
Alec Armbruster
d8ee0ec78a
change max-age to 5 for non-authed responses 2023-06-29 13:33:30 -04:00
Alec Armbruster
fead020bdc
Fix PostListing mobile margin layout issue (#1706) 2023-06-29 17:28:55 +00:00
SleeplessOne1917
339cefa2b0 Cache static data for a day 2023-06-29 13:14:48 -04:00
Dessalines
08370d4c4e Try increasing node memory. 2023-06-29 11:12:12 -04:00
Dessalines
b73cb808e4 v0.18.1-rc.4 2023-06-29 10:40:19 -04:00
Alec Armbruster
80d9aac1ca
Fix taglines on Home (#1701)
* fix taglines on home

* fix error on admin panel
2023-06-29 10:38:35 -04:00
SleeplessOne1917
751495702c
Use git hash to break cache (#1684)
* Use git hash to break cache

* Address PR feedback

* Make hash docker agnostic

* Add trailing slash

* Update .prettierignore

Co-authored-by: Alec Armbruster <35377827+alectrocute@users.noreply.github.com>

* Remove debugging log

* implement getStaticDir util

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
Co-authored-by: Alec Armbruster <35377827+alectrocute@users.noreply.github.com>
2023-06-29 10:29:33 -04:00
SleeplessOne1917
ad6db69dda
Merge pull request #1698 from LemmyNet/fix_datetime_2
Removing unecessary timezone adjusting
2023-06-29 09:42:57 -04:00
SleeplessOne1917
6bf159261a
Merge branch 'main' into fix_datetime_2 2023-06-29 09:36:24 -04:00
Alec Armbruster
73be96880a
fix issue with thumbnails (#1695) 2023-06-29 09:34:47 -04:00
Alec Armbruster
dc439b8dee
Merge branch 'main' into fix_datetime_2 2023-06-29 09:32:03 -04:00
Dessalines
ec649d1911
Fixing site setup. Fixes #1694 (#1697) 2023-06-29 09:04:09 -04:00
Dessalines
eee1f443a8 Removing unecessary timezone adjusting 2023-06-29 08:59:54 -04:00
SleeplessOne1917
67365d5878
Merge pull request #1685 from Raicuparta/community-search
Communities page: make search type default to "Communities'
2023-06-29 07:55:05 -04:00
Alec Armbruster
d9d4a3195f
Merge branch 'main' into community-search 2023-06-29 07:31:50 -04:00
SleeplessOne1917
005135d4f2
Allow audio captcha through CSP (#1690)
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2023-06-29 07:20:45 -04:00
Raicuparta
75c2e803c9 fix formatting 2023-06-29 13:12:30 +02:00
SleeplessOne1917
500003f310
Merge branch 'main' into community-search 2023-06-29 06:07:44 -04:00
Raicuparta
6305f593d0
Merge branch 'main' into community-search 2023-06-29 04:22:07 +01:00
Raicuparta
92f6fe914f
Search only communities by default when coming from communities page 2023-06-29 04:12:16 +01:00
21 changed files with 130 additions and 94 deletions

View file

@ -2,3 +2,4 @@ src/shared/translations
lemmy-translations
src/assets/css/themes/*.css
stats.json
dist

View file

@ -27,7 +27,7 @@ COPY .git .git
RUN echo "export const VERSION = '$(git describe --tag)';" > "src/shared/version.ts"
RUN yarn --production --prefer-offline
RUN yarn build:prod
RUN NODE_OPTIONS="--max-old-space-size=8192" yarn build:prod
# Prune the image
RUN node-prune /usr/src/app/node_modules

View file

@ -20,6 +20,7 @@ COPY generate_translations.js \
COPY lemmy-translations lemmy-translations
COPY src src
COPY .git .git
# Set UI version
RUN echo "export const VERSION = 'dev';" > "src/shared/version.ts"

View file

@ -1,6 +1,6 @@
{
"name": "lemmy-ui",
"version": "0.18.1-rc.3",
"version": "0.18.1-rc.5",
"description": "An isomorphic UI for lemmy",
"repository": "https://github.com/LemmyNet/lemmy-ui",
"license": "AGPL-3.0",
@ -8,9 +8,9 @@
"scripts": {
"analyze": "webpack --mode=none",
"prebuild:dev": "yarn clean && node generate_translations.js",
"build:dev": "webpack --mode=development",
"build:dev": "webpack --env COMMIT_HASH=$(git rev-parse --short HEAD) --mode=development",
"prebuild:prod": "yarn clean && node generate_translations.js",
"build:prod": "webpack --mode=production",
"build:prod": "webpack --env COMMIT_HASH=$(git rev-parse --short HEAD) --mode=production",
"clean": "yarn run rimraf dist",
"dev": "yarn build:dev --watch",
"lint": "yarn translations:generate && tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx \"src/**\" && prettier --check \"src/**/*.{ts,tsx,js,css,scss}\"",
@ -52,7 +52,6 @@
"cross-fetch": "^3.1.5",
"css-loader": "^6.7.3",
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.0",
"emoji-mart": "^5.4.0",
"emoji-short-name": "^2.0.0",
"express": "~4.18.2",

View file

@ -1,4 +1,5 @@
import { setupDateFns } from "@utils/app";
import { getStaticDir } from "@utils/env";
import express from "express";
import path from "path";
import process from "process";
@ -19,7 +20,13 @@ const [hostname, port] = process.env["LEMMY_UI_HOST"]
server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.use("/static", express.static(path.resolve("./dist")));
server.use(
getStaticDir(),
express.static(path.resolve("./dist"), {
maxAge: 24 * 60 * 60 * 1000, // 1 day
immutable: true,
})
);
server.use(setCacheControl);
if (!process.env["LEMMY_UI_DISABLE_CSP"] && !process.env["LEMMY_UI_DEBUG"]) {

View file

@ -1,4 +1,4 @@
import type { NextFunction, Response } from "express";
import type { NextFunction, Request, Response } from "express";
import { UserService } from "../shared/services";
export function setDefaultCsp({
@ -10,7 +10,7 @@ export function setDefaultCsp({
}) {
res.setHeader(
"Content-Security-Policy",
`default-src 'self'; manifest-src *; connect-src *; img-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; form-action 'self'; base-uri 'self'; frame-src *; media-src *`
`default-src 'self'; manifest-src *; connect-src *; img-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; form-action 'self'; base-uri 'self'; frame-src *; media-src * data:`
);
next();
@ -18,24 +18,33 @@ export function setDefaultCsp({
// Set cache-control headers. If user is logged in, set `private` to prevent storing data in
// shared caches (eg nginx) and leaking of private data. If user is not logged in, allow caching
// all responses for 60 seconds to reduce load on backend and database. The specific cache
// all responses for 5 seconds to reduce load on backend and database. The specific cache
// interval is rather arbitrary and could be set higher (less server load) or lower (fresher data).
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
export function setCacheControl({
res,
next,
}: {
res: Response;
next: NextFunction;
}) {
export function setCacheControl(
req: Request,
res: Response,
next: NextFunction
) {
const user = UserService.Instance;
let caching;
if (user.auth()) {
caching = "private";
let caching: string;
if (
process.env.NODE_ENV === "production" &&
(req.path.match(/\.(js|css|txt|manifest\.webmanifest)\/?$/) ||
req.path.includes("/css/themelist"))
) {
// Static content gets cached publicly for a day
caching = "public, max-age=86400";
} else {
caching = "public, max-age=60";
if (user.auth()) {
caching = "private";
} else {
caching = "public, max-age=5";
}
}
res.setHeader("Cache-Control", caching);
next();

View file

@ -1,3 +1,4 @@
import { getStaticDir } from "@utils/env";
import { Helmet } from "inferno-helmet";
import { renderToString } from "inferno-server";
import serialize from "serialize-javascript";
@ -23,7 +24,7 @@ export async function createSsrHtml(
if (!appleTouchIcon) {
appleTouchIcon = site?.site_view.site.icon
? `data:image/png;base64,${sharp(
? `data:image/png;base64,${await sharp(
await fetchIconPng(site.site_view.site.icon)
)
.resize(180, 180)
@ -87,7 +88,7 @@ export async function createSsrHtml(
<link rel="apple-touch-startup-image" href=${appleTouchIcon} />
<!-- Styles -->
<link rel="stylesheet" type="text/css" href="/static/styles/styles.css" />
<link rel="stylesheet" type="text/css" href="${getStaticDir()}/styles/styles.css" />
<!-- Current theme and more -->
${helmet.link.toString() || fallbackTheme}
@ -102,7 +103,7 @@ export async function createSsrHtml(
</noscript>
<div id='root'>${root}</div>
<script defer src='/static/js/client.js'></script>
<script defer src='${getStaticDir()}/js/client.js'></script>
</body>
</html>
`;

View file

@ -1,3 +1,4 @@
import { getStaticDir } from "@utils/env";
import classNames from "classnames";
import { Component } from "inferno";
import { I18NextService } from "../../services";
@ -23,7 +24,9 @@ export class Icon extends Component<IconProps, any> {
})}
>
<use
xlinkHref={`/static/assets/symbols.svg#icon-${this.props.icon}`}
xlinkHref={`${getStaticDir()}/assets/symbols.svg#icon-${
this.props.icon
}`}
></use>
<div className="visually-hidden">
<title>{this.props.icon}</title>

View file

@ -1,5 +1,5 @@
import { capitalizeFirstLetter, formatPastDate } from "@utils/helpers";
import { formatInTimeZone } from "date-fns-tz";
import { format } from "date-fns";
import parseISO from "date-fns/parseISO";
import { Component } from "inferno";
import { I18NextService } from "../../services";
@ -13,9 +13,8 @@ interface MomentTimeProps {
}
function formatDate(input: string) {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
const parsed = parseISO(input + "Z");
return formatInTimeZone(parsed, tz, "PPPPpppp");
return format(parsed, "PPPPpppp");
}
export class MomentTime extends Component<MomentTimeProps, any> {

View file

@ -284,7 +284,9 @@ export class Communities extends Component<any, CommunitiesState> {
handleSearchSubmit(i: Communities, event: any) {
event.preventDefault();
const searchParamEncoded = encodeURIComponent(i.state.searchText);
i.context.router.history.push(`/search?q=${searchParamEncoded}`);
i.context.router.history.push(
`/search?q=${searchParamEncoded}&type=Communities`
);
}
static async fetchInitialData({

View file

@ -279,13 +279,15 @@ export class Home extends Component<any, HomeState> {
trendingCommunitiesRes,
commentsRes,
postsRes,
tagline: getRandomFromList(this.state?.siteRes?.taglines ?? [])
?.content,
isIsomorphic: true,
};
HomeCacheService.postsRes = postsRes;
}
this.state.tagline = getRandomFromList(
this.state?.siteRes?.taglines ?? []
)?.content;
}
componentWillUnmount() {

View file

@ -205,9 +205,7 @@ export class Setup extends Component<any, State> {
const data = i.state.registerRes.data;
UserService.Instance.login(data);
if (UserService.Instance.jwtInfo) {
i.setState({ doneRegisteringUser: true });
}
i.setState({ doneRegisteringUser: true });
}
}
}

View file

@ -141,7 +141,7 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
handleEditTaglineClick(d: { i: TaglineForm; index: number }, event: any) {
event.preventDefault();
if (this.state.editingRow == d.index) {
if (d.i.state.editingRow == d.index) {
d.i.setState({ editingRow: undefined });
} else {
d.i.setState({ editingRow: d.index });

View file

@ -1,4 +1,5 @@
import { showAvatars } from "@utils/app";
import { getStaticDir } from "@utils/env";
import { hostname, isCakeDay } from "@utils/helpers";
import classNames from "classnames";
import { Component } from "inferno";
@ -88,7 +89,7 @@ export class PersonListing extends Component<PersonListingProps, any> {
!this.props.person.banned &&
showAvatars() && (
<PictrsImage
src={avatar ?? "/static/assets/icons/icon-96x96.png"}
src={avatar ?? `${getStaticDir()}/assets/icons/icon-96x96.png`}
icon
/>
)}

View file

@ -333,7 +333,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
return (
<button
type="button"
className="thumbnail rounded overflow-hidden d-inline-block position-relative mb-2 p-0 border-0"
className="thumbnail rounded overflow-hidden d-inline-block position-relative p-0 border-0"
data-tippy-content={I18NextService.i18n.t("expand_here")}
onClick={linkEvent(this, this.handleImageExpandClick)}
aria-label={I18NextService.i18n.t("expand_here")}
@ -348,7 +348,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
} else if (!this.props.hideImage && url && thumbnail && this.imageSrc) {
return (
<a
className="thumbnail rounded bg-light d-flex justify-content-center"
className="thumbnail rounded overflow-hidden d-inline-block position-relative p-0 border-0"
href={url}
rel={relTags}
title={url}
@ -403,8 +403,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
createdLine() {
const post_view = this.postView;
return (
<div className="small">
<div className="small mb-1 mb-md-0">
<span className="me-1">
<PersonListing person={post_view.creator} />
</span>

View file

@ -1,5 +1,7 @@
export const favIconUrl = "/static/assets/icons/favicon.svg";
export const favIconPngUrl = "/static/assets/icons/apple-touch-icon.png";
import { getStaticDir } from "@utils/env";
export const favIconUrl = `${getStaticDir()}/assets/icons/favicon.svg`;
export const favIconPngUrl = `${getStaticDir()}/assets/icons/apple-touch-icon.png`;
export const repoUrl = "https://github.com/LemmyNet";
export const joinLemmyUrl = "https://join-lemmy.org";

View file

@ -1,5 +1,5 @@
export default function isAuthPath(pathname: string) {
return /create_.*|inbox|settings|admin|reports|registration_applications/g.test(
return /^\/create_.*|inbox|settings|admin|reports|registration_applications/g.test(
pathname
);
}

View file

@ -0,0 +1,5 @@
// Returns path to static directory, intended
// for cache-busting based on latest commit hash.
export default function getStaticDir() {
return `/static/${process.env.COMMIT_HASH}`;
}

View file

@ -6,6 +6,7 @@ import getHttpBaseExternal from "./get-http-base-external";
import getHttpBaseInternal from "./get-http-base-internal";
import getInternalHost from "./get-internal-host";
import getSecure from "./get-secure";
import getStaticDir from "./get-static-dir";
import httpExternalPath from "./http-external-path";
import isHttps from "./is-https";
@ -18,6 +19,7 @@ export {
getHttpBaseInternal,
getInternalHost,
getSecure,
getStaticDir,
httpExternalPath,
isHttps,
};

View file

@ -14,56 +14,63 @@ const banner = `
@license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL v3.0
`;
const base = {
output: {
filename: "js/server.js",
publicPath: "/",
hashFunction: "xxhash64",
},
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
alias: {
"@": path.resolve(__dirname, "src/"),
"@utils": path.resolve(__dirname, "src/shared/utils/"),
function getBase(env, mode) {
return {
output: {
filename: "js/server.js",
publicPath: "/",
hashFunction: "xxhash64",
},
},
performance: {
hints: false,
},
module: {
rules: [
{
test: /\.(scss|css)$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
alias: {
"@": path.resolve(__dirname, "src/"),
"@utils": path.resolve(__dirname, "src/shared/utils/"),
},
{
test: /\.(js|jsx|tsx|ts)$/, // All ts and tsx files will be process by
exclude: /node_modules/, // ignore node_modules
loader: "babel-loader",
},
// Due to some weird babel issue: https://github.com/webpack/webpack/issues/11467
{
test: /\.m?js/,
resolve: {
fullySpecified: false,
},
performance: {
hints: false,
},
module: {
rules: [
{
test: /\.(scss|css)$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
},
{
test: /\.(js|jsx|tsx|ts)$/, // All ts and tsx files will be process by
exclude: /node_modules/, // ignore node_modules
loader: "babel-loader",
},
// Due to some weird babel issue: https://github.com/webpack/webpack/issues/11467
{
test: /\.m?js/,
resolve: {
fullySpecified: false,
},
},
],
},
plugins: [
new webpack.DefinePlugin({
"process.env.COMMIT_HASH": `"${env.COMMIT_HASH}"`,
"process.env.NODE_ENV": `"${mode}"`,
}),
new MiniCssExtractPlugin({
filename: "styles/styles.css",
}),
new CopyPlugin({
patterns: [{ from: "./src/assets", to: "./assets" }],
}),
new webpack.BannerPlugin({
banner,
}),
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "styles/styles.css",
}),
new CopyPlugin({
patterns: [{ from: "./src/assets", to: "./assets" }],
}),
new webpack.BannerPlugin({
banner,
}),
],
};
};
}
const createServerConfig = (_env, mode) => {
const createServerConfig = (env, mode) => {
const base = getBase(env, mode);
const config = merge({}, base, {
mode,
entry: "./src/server/index.tsx",
@ -90,13 +97,14 @@ const createServerConfig = (_env, mode) => {
return config;
};
const createClientConfig = (_env, mode) => {
const createClientConfig = (env, mode) => {
const base = getBase(env, mode);
const config = merge({}, base, {
mode,
entry: "./src/client/index.tsx",
output: {
filename: "js/client.js",
publicPath: "/static/",
publicPath: `/static/${env.COMMIT_HASH}/`,
},
plugins: [
...base.plugins,
@ -104,7 +112,7 @@ const createClientConfig = (_env, mode) => {
enableInDevelopment: mode !== "development", // this may seem counterintuitive, but it is correct
workbox: {
modifyURLPrefix: {
"/": "/static/",
"/": `/static/${env.COMMIT_HASH}/`,
},
cacheId: "lemmy",
include: [/(assets|styles|js)\/.+\..+$/g],

View file

@ -3237,11 +3237,6 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
date-fns-tz@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-2.0.0.tgz#1b14c386cb8bc16fc56fe333d4fc34ae1d1099d5"
integrity sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ==
date-fns@^2.30.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"