Compare commits
1 commit
main
...
fix_site_s
Author | SHA1 | Date | |
---|---|---|---|
![]() |
29feb58d2f |
19 changed files with 88 additions and 124 deletions
|
@ -2,4 +2,3 @@ src/shared/translations
|
|||
lemmy-translations
|
||||
src/assets/css/themes/*.css
|
||||
stats.json
|
||||
dist
|
||||
|
|
|
@ -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 NODE_OPTIONS="--max-old-space-size=8192" yarn build:prod
|
||||
RUN yarn build:prod
|
||||
|
||||
# Prune the image
|
||||
RUN node-prune /usr/src/app/node_modules
|
||||
|
|
|
@ -20,7 +20,6 @@ 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"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "lemmy-ui",
|
||||
"version": "0.18.1-rc.5",
|
||||
"version": "0.18.1-rc.3",
|
||||
"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 --env COMMIT_HASH=$(git rev-parse --short HEAD) --mode=development",
|
||||
"build:dev": "webpack --mode=development",
|
||||
"prebuild:prod": "yarn clean && node generate_translations.js",
|
||||
"build:prod": "webpack --env COMMIT_HASH=$(git rev-parse --short HEAD) --mode=production",
|
||||
"build:prod": "webpack --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,6 +52,7 @@
|
|||
"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",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { setupDateFns } from "@utils/app";
|
||||
import { getStaticDir } from "@utils/env";
|
||||
import express from "express";
|
||||
import path from "path";
|
||||
import process from "process";
|
||||
|
@ -20,13 +19,7 @@ const [hostname, port] = process.env["LEMMY_UI_HOST"]
|
|||
|
||||
server.use(express.json());
|
||||
server.use(express.urlencoded({ extended: false }));
|
||||
server.use(
|
||||
getStaticDir(),
|
||||
express.static(path.resolve("./dist"), {
|
||||
maxAge: 24 * 60 * 60 * 1000, // 1 day
|
||||
immutable: true,
|
||||
})
|
||||
);
|
||||
server.use("/static", express.static(path.resolve("./dist")));
|
||||
server.use(setCacheControl);
|
||||
|
||||
if (!process.env["LEMMY_UI_DISABLE_CSP"] && !process.env["LEMMY_UI_DEBUG"]) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { NextFunction, Request, Response } from "express";
|
||||
import type { NextFunction, Response } from "express";
|
||||
import { UserService } from "../shared/services";
|
||||
|
||||
export function setDefaultCsp({
|
||||
|
@ -18,33 +18,24 @@ 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 5 seconds to reduce load on backend and database. The specific cache
|
||||
// all responses for 60 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(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
export function setCacheControl({
|
||||
res,
|
||||
next,
|
||||
}: {
|
||||
res: Response;
|
||||
next: NextFunction;
|
||||
}) {
|
||||
const user = UserService.Instance;
|
||||
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";
|
||||
if (user.auth()) {
|
||||
caching = "private";
|
||||
} else {
|
||||
if (user.auth()) {
|
||||
caching = "private";
|
||||
} else {
|
||||
caching = "public, max-age=5";
|
||||
}
|
||||
caching = "public, max-age=60";
|
||||
}
|
||||
|
||||
res.setHeader("Cache-Control", caching);
|
||||
|
||||
next();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { getStaticDir } from "@utils/env";
|
||||
import { Helmet } from "inferno-helmet";
|
||||
import { renderToString } from "inferno-server";
|
||||
import serialize from "serialize-javascript";
|
||||
|
@ -24,7 +23,7 @@ export async function createSsrHtml(
|
|||
|
||||
if (!appleTouchIcon) {
|
||||
appleTouchIcon = site?.site_view.site.icon
|
||||
? `data:image/png;base64,${await sharp(
|
||||
? `data:image/png;base64,${sharp(
|
||||
await fetchIconPng(site.site_view.site.icon)
|
||||
)
|
||||
.resize(180, 180)
|
||||
|
@ -88,7 +87,7 @@ export async function createSsrHtml(
|
|||
<link rel="apple-touch-startup-image" href=${appleTouchIcon} />
|
||||
|
||||
<!-- Styles -->
|
||||
<link rel="stylesheet" type="text/css" href="${getStaticDir()}/styles/styles.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/styles/styles.css" />
|
||||
|
||||
<!-- Current theme and more -->
|
||||
${helmet.link.toString() || fallbackTheme}
|
||||
|
@ -103,7 +102,7 @@ export async function createSsrHtml(
|
|||
</noscript>
|
||||
|
||||
<div id='root'>${root}</div>
|
||||
<script defer src='${getStaticDir()}/js/client.js'></script>
|
||||
<script defer src='/static/js/client.js'></script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { getStaticDir } from "@utils/env";
|
||||
import classNames from "classnames";
|
||||
import { Component } from "inferno";
|
||||
import { I18NextService } from "../../services";
|
||||
|
@ -24,9 +23,7 @@ export class Icon extends Component<IconProps, any> {
|
|||
})}
|
||||
>
|
||||
<use
|
||||
xlinkHref={`${getStaticDir()}/assets/symbols.svg#icon-${
|
||||
this.props.icon
|
||||
}`}
|
||||
xlinkHref={`/static/assets/symbols.svg#icon-${this.props.icon}`}
|
||||
></use>
|
||||
<div className="visually-hidden">
|
||||
<title>{this.props.icon}</title>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { capitalizeFirstLetter, formatPastDate } from "@utils/helpers";
|
||||
import { format } from "date-fns";
|
||||
import { formatInTimeZone } from "date-fns-tz";
|
||||
import parseISO from "date-fns/parseISO";
|
||||
import { Component } from "inferno";
|
||||
import { I18NextService } from "../../services";
|
||||
|
@ -13,8 +13,9 @@ interface MomentTimeProps {
|
|||
}
|
||||
|
||||
function formatDate(input: string) {
|
||||
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
const parsed = parseISO(input + "Z");
|
||||
return format(parsed, "PPPPpppp");
|
||||
return formatInTimeZone(parsed, tz, "PPPPpppp");
|
||||
}
|
||||
|
||||
export class MomentTime extends Component<MomentTimeProps, any> {
|
||||
|
|
|
@ -279,15 +279,13 @@ 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() {
|
||||
|
|
|
@ -141,7 +141,7 @@ export class TaglineForm extends Component<TaglineFormProps, TaglineFormState> {
|
|||
|
||||
handleEditTaglineClick(d: { i: TaglineForm; index: number }, event: any) {
|
||||
event.preventDefault();
|
||||
if (d.i.state.editingRow == d.index) {
|
||||
if (this.state.editingRow == d.index) {
|
||||
d.i.setState({ editingRow: undefined });
|
||||
} else {
|
||||
d.i.setState({ editingRow: d.index });
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { showAvatars } from "@utils/app";
|
||||
import { getStaticDir } from "@utils/env";
|
||||
import { hostname, isCakeDay } from "@utils/helpers";
|
||||
import classNames from "classnames";
|
||||
import { Component } from "inferno";
|
||||
|
@ -89,7 +88,7 @@ export class PersonListing extends Component<PersonListingProps, any> {
|
|||
!this.props.person.banned &&
|
||||
showAvatars() && (
|
||||
<PictrsImage
|
||||
src={avatar ?? `${getStaticDir()}/assets/icons/icon-96x96.png`}
|
||||
src={avatar ?? "/static/assets/icons/icon-96x96.png"}
|
||||
icon
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -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 p-0 border-0"
|
||||
className="thumbnail rounded overflow-hidden d-inline-block position-relative mb-2 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 overflow-hidden d-inline-block position-relative p-0 border-0"
|
||||
className="thumbnail rounded bg-light d-flex justify-content-center"
|
||||
href={url}
|
||||
rel={relTags}
|
||||
title={url}
|
||||
|
@ -403,9 +403,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
|
|||
|
||||
createdLine() {
|
||||
const post_view = this.postView;
|
||||
|
||||
return (
|
||||
<div className="small mb-1 mb-md-0">
|
||||
<div className="small">
|
||||
<span className="me-1">
|
||||
<PersonListing person={post_view.creator} />
|
||||
</span>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
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 favIconUrl = "/static/assets/icons/favicon.svg";
|
||||
export const favIconPngUrl = "/static/assets/icons/apple-touch-icon.png";
|
||||
|
||||
export const repoUrl = "https://github.com/LemmyNet";
|
||||
export const joinLemmyUrl = "https://join-lemmy.org";
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
5
src/shared/utils/env/get-static-dir.ts
vendored
5
src/shared/utils/env/get-static-dir.ts
vendored
|
@ -1,5 +0,0 @@
|
|||
// Returns path to static directory, intended
|
||||
// for cache-busting based on latest commit hash.
|
||||
export default function getStaticDir() {
|
||||
return `/static/${process.env.COMMIT_HASH}`;
|
||||
}
|
2
src/shared/utils/env/index.ts
vendored
2
src/shared/utils/env/index.ts
vendored
|
@ -6,7 +6,6 @@ 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";
|
||||
|
||||
|
@ -19,7 +18,6 @@ export {
|
|||
getHttpBaseInternal,
|
||||
getInternalHost,
|
||||
getSecure,
|
||||
getStaticDir,
|
||||
httpExternalPath,
|
||||
isHttps,
|
||||
};
|
||||
|
|
|
@ -14,63 +14,56 @@ const banner = `
|
|||
@license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL v3.0
|
||||
`;
|
||||
|
||||
function getBase(env, mode) {
|
||||
return {
|
||||
output: {
|
||||
filename: "js/server.js",
|
||||
publicPath: "/",
|
||||
hashFunction: "xxhash64",
|
||||
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/"),
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".jsx", ".ts", ".tsx"],
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src/"),
|
||||
"@utils": path.resolve(__dirname, "src/shared/utils/"),
|
||||
},
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(scss|css)$/i,
|
||||
use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
|
||||
},
|
||||
},
|
||||
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,
|
||||
},
|
||||
{
|
||||
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 base = getBase(env, mode);
|
||||
const createServerConfig = (_env, mode) => {
|
||||
const config = merge({}, base, {
|
||||
mode,
|
||||
entry: "./src/server/index.tsx",
|
||||
|
@ -97,14 +90,13 @@ const createServerConfig = (env, mode) => {
|
|||
return config;
|
||||
};
|
||||
|
||||
const createClientConfig = (env, mode) => {
|
||||
const base = getBase(env, mode);
|
||||
const createClientConfig = (_env, mode) => {
|
||||
const config = merge({}, base, {
|
||||
mode,
|
||||
entry: "./src/client/index.tsx",
|
||||
output: {
|
||||
filename: "js/client.js",
|
||||
publicPath: `/static/${env.COMMIT_HASH}/`,
|
||||
publicPath: "/static/",
|
||||
},
|
||||
plugins: [
|
||||
...base.plugins,
|
||||
|
@ -112,7 +104,7 @@ const createClientConfig = (env, mode) => {
|
|||
enableInDevelopment: mode !== "development", // this may seem counterintuitive, but it is correct
|
||||
workbox: {
|
||||
modifyURLPrefix: {
|
||||
"/": `/static/${env.COMMIT_HASH}/`,
|
||||
"/": "/static/",
|
||||
},
|
||||
cacheId: "lemmy",
|
||||
include: [/(assets|styles|js)\/.+\..+$/g],
|
||||
|
|
|
@ -3237,6 +3237,11 @@ 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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue