mirror of
https://github.com/outline/outline.git
synced 2026-06-13 19:35:02 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e9beac59f | |||
| a0f7c76405 |
+1
-1
@@ -30,7 +30,7 @@ REDIS_URL=redis://localhost:6379
|
|||||||
|
|
||||||
# URL should point to the fully qualified, publicly accessible URL. If using a
|
# URL should point to the fully qualified, publicly accessible URL. If using a
|
||||||
# proxy the port in URL and PORT may be different.
|
# proxy the port in URL and PORT may be different.
|
||||||
URL=http://localhost:3000
|
URL=https://app.outline.dev:3000
|
||||||
PORT=3000
|
PORT=3000
|
||||||
|
|
||||||
# See [documentation](docs/SERVICES.md) on running a separate collaboration
|
# See [documentation](docs/SERVICES.md) on running a separate collaboration
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
up:
|
up:
|
||||||
docker-compose up -d redis postgres s3
|
docker-compose up -d redis postgres s3
|
||||||
|
yarn install-local-ssl
|
||||||
yarn install --pure-lockfile
|
yarn install --pure-lockfile
|
||||||
yarn dev:watch
|
yarn dev:watch
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
"lint": "eslint app server shared plugins",
|
"lint": "eslint app server shared plugins",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"postinstall": "yarn patch-package",
|
"postinstall": "yarn patch-package",
|
||||||
|
"install-local-ssl": "node ./server/scripts/install-local-ssl.js",
|
||||||
"heroku-postbuild": "yarn build && yarn db:migrate",
|
"heroku-postbuild": "yarn build && yarn db:migrate",
|
||||||
"db:create-migration": "sequelize migration:create",
|
"db:create-migration": "sequelize migration:create",
|
||||||
"db:create": "sequelize db:create",
|
"db:create": "sequelize db:create",
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ describe("email", () => {
|
|||||||
email: user.email,
|
email: user.email,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
@@ -71,7 +71,7 @@ describe("email", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not send email when user is on another subdomain but respond with success", async () => {
|
it("should not send email when user is on another subdomain but respond with success", async () => {
|
||||||
env.URL = sharedEnv.URL = "http://localoutline.com";
|
env.URL = sharedEnv.URL = "https://app.outline.dev";
|
||||||
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
||||||
env.DEPLOYMENT = "hosted";
|
env.DEPLOYMENT = "hosted";
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ describe("email", () => {
|
|||||||
email: user.email,
|
email: user.email,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ describe("email", () => {
|
|||||||
email: user.email,
|
email: user.email,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
@@ -129,7 +129,7 @@ describe("email", () => {
|
|||||||
email: "user@example.com",
|
email: "user@example.com",
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
@@ -141,7 +141,7 @@ describe("email", () => {
|
|||||||
describe("with multiple users matching email", () => {
|
describe("with multiple users matching email", () => {
|
||||||
it("should default to current subdomain with SSO", async () => {
|
it("should default to current subdomain with SSO", async () => {
|
||||||
const spy = jest.spyOn(SigninEmail.prototype, "schedule");
|
const spy = jest.spyOn(SigninEmail.prototype, "schedule");
|
||||||
env.URL = sharedEnv.URL = "http://localoutline.com";
|
env.URL = sharedEnv.URL = "https://app.outline.dev";
|
||||||
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
||||||
const email = "sso-user@example.org";
|
const email = "sso-user@example.org";
|
||||||
const team = await buildTeam({
|
const team = await buildTeam({
|
||||||
@@ -159,7 +159,7 @@ describe("email", () => {
|
|||||||
email,
|
email,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
@@ -171,7 +171,7 @@ describe("email", () => {
|
|||||||
|
|
||||||
it("should default to current subdomain with guest email", async () => {
|
it("should default to current subdomain with guest email", async () => {
|
||||||
const spy = jest.spyOn(SigninEmail.prototype, "schedule");
|
const spy = jest.spyOn(SigninEmail.prototype, "schedule");
|
||||||
env.URL = sharedEnv.URL = "http://localoutline.com";
|
env.URL = sharedEnv.URL = "https://app.outline.dev";
|
||||||
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
||||||
const email = "guest-user@example.org";
|
const email = "guest-user@example.org";
|
||||||
const team = await buildTeam({
|
const team = await buildTeam({
|
||||||
@@ -189,7 +189,7 @@ describe("email", () => {
|
|||||||
email,
|
email,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
|
|||||||
+1
-1
@@ -162,7 +162,7 @@ export class Environment {
|
|||||||
*/
|
*/
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
public PORT = this.toOptionalNumber(process.env.PORT);
|
public PORT = this.toOptionalNumber(process.env.PORT) ?? 3000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional extra debugging. Comma separated
|
* Optional extra debugging. Comma separated
|
||||||
|
|||||||
+4
-3
@@ -128,16 +128,17 @@ async function start(id: number, disconnect: () => void) {
|
|||||||
});
|
});
|
||||||
server.on("listening", () => {
|
server.on("listening", () => {
|
||||||
const address = server.address();
|
const address = server.address();
|
||||||
|
const port = (address as AddressInfo).port;
|
||||||
|
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"lifecycle",
|
"lifecycle",
|
||||||
`Listening on ${useHTTPS ? "https" : "http"}://localhost:${
|
`Listening on ${useHTTPS ? "https" : "http"}://localhost:${port} / ${
|
||||||
(address as AddressInfo).port
|
env.URL
|
||||||
}`
|
}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(normalizedPortFlag || env.PORT || "3000");
|
server.listen(normalizedPortFlag || env.PORT);
|
||||||
server.setTimeout(env.REQUEST_TIMEOUT);
|
server.setTimeout(env.REQUEST_TIMEOUT);
|
||||||
|
|
||||||
ShutdownHelper.add(
|
ShutdownHelper.add(
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ describe("#auth.config", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return available providers for team subdomain", async () => {
|
it("should return available providers for team subdomain", async () => {
|
||||||
env.URL = sharedEnv.URL = "http://localoutline.com";
|
env.URL = sharedEnv.URL = "https://app.outline.dev";
|
||||||
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
env.SUBDOMAINS_ENABLED = sharedEnv.SUBDOMAINS_ENABLED = true;
|
||||||
env.DEPLOYMENT = "hosted";
|
env.DEPLOYMENT = "hosted";
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ describe("#auth.config", () => {
|
|||||||
});
|
});
|
||||||
const res = await server.post("/api/auth.config", {
|
const res = await server.post("/api/auth.config", {
|
||||||
headers: {
|
headers: {
|
||||||
host: `example.localoutline.com`,
|
host: `example.outline.dev`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
@@ -155,7 +155,7 @@ describe("#auth.config", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return email provider for team when guest signin enabled", async () => {
|
it("should return email provider for team when guest signin enabled", async () => {
|
||||||
env.URL = sharedEnv.URL = "http://localoutline.com";
|
env.URL = sharedEnv.URL = "https://app.outline.dev";
|
||||||
env.DEPLOYMENT = "hosted";
|
env.DEPLOYMENT = "hosted";
|
||||||
|
|
||||||
await buildTeam({
|
await buildTeam({
|
||||||
@@ -170,7 +170,7 @@ describe("#auth.config", () => {
|
|||||||
});
|
});
|
||||||
const res = await server.post("/api/auth.config", {
|
const res = await server.post("/api/auth.config", {
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
@@ -181,7 +181,7 @@ describe("#auth.config", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not return provider when disabled", async () => {
|
it("should not return provider when disabled", async () => {
|
||||||
env.URL = sharedEnv.URL = "http://localoutline.com";
|
env.URL = sharedEnv.URL = "https://app.outline.dev";
|
||||||
env.DEPLOYMENT = "hosted";
|
env.DEPLOYMENT = "hosted";
|
||||||
|
|
||||||
await buildTeam({
|
await buildTeam({
|
||||||
@@ -197,7 +197,7 @@ describe("#auth.config", () => {
|
|||||||
});
|
});
|
||||||
const res = await server.post("/api/auth.config", {
|
const res = await server.post("/api/auth.config", {
|
||||||
headers: {
|
headers: {
|
||||||
host: "example.localoutline.com",
|
host: "example.outline.dev",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const body = await res.json();
|
const body = await res.json();
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ import readManifestFile from "@server/utils/readManifestFile";
|
|||||||
const isProduction = env.ENVIRONMENT === "production";
|
const isProduction = env.ENVIRONMENT === "production";
|
||||||
const isDevelopment = env.ENVIRONMENT === "development";
|
const isDevelopment = env.ENVIRONMENT === "development";
|
||||||
const isTest = env.ENVIRONMENT === "test";
|
const isTest = env.ENVIRONMENT === "test";
|
||||||
|
|
||||||
const readFile = util.promisify(fs.readFile);
|
const readFile = util.promisify(fs.readFile);
|
||||||
|
const entry = "app/index.tsx";
|
||||||
|
const viteHost = env.URL.replace(`:${env.PORT}`, ":3001");
|
||||||
|
|
||||||
let indexHtmlCache: Buffer | undefined;
|
let indexHtmlCache: Buffer | undefined;
|
||||||
|
|
||||||
const readIndexFile = async (): Promise<Buffer> => {
|
const readIndexFile = async (): Promise<Buffer> => {
|
||||||
@@ -71,20 +75,20 @@ export const renderApp = async (
|
|||||||
window.env = ${JSON.stringify(presentEnv(env, options.analytics))};
|
window.env = ${JSON.stringify(presentEnv(env, options.analytics))};
|
||||||
</script>
|
</script>
|
||||||
`;
|
`;
|
||||||
const entry = "app/index.tsx";
|
|
||||||
const scriptTags = isProduction
|
const scriptTags = isProduction
|
||||||
? `<script type="module" nonce="${ctx.state.cspNonce}" src="${
|
? `<script type="module" nonce="${ctx.state.cspNonce}" src="${
|
||||||
env.CDN_URL || ""
|
env.CDN_URL || ""
|
||||||
}/static/${readManifestFile()[entry]["file"]}"></script>`
|
}/static/${readManifestFile()[entry]["file"]}"></script>`
|
||||||
: `<script type="module" nonce="${ctx.state.cspNonce}">
|
: `<script type="module" nonce="${ctx.state.cspNonce}">
|
||||||
import RefreshRuntime from 'http://localhost:3001/static/@react-refresh'
|
import RefreshRuntime from "${viteHost}/static/@react-refresh"
|
||||||
RefreshRuntime.injectIntoGlobalHook(window)
|
RefreshRuntime.injectIntoGlobalHook(window)
|
||||||
window.$RefreshReg$ = () => { }
|
window.$RefreshReg$ = () => { }
|
||||||
window.$RefreshSig$ = () => (type) => type
|
window.$RefreshSig$ = () => (type) => type
|
||||||
window.__vite_plugin_react_preamble_installed__ = true
|
window.__vite_plugin_react_preamble_installed__ = true
|
||||||
</script>
|
</script>
|
||||||
<script type="module" nonce="${ctx.state.cspNonce}" src="http://localhost:3001/static/@vite/client"></script>
|
<script type="module" nonce="${ctx.state.cspNonce}" src="${viteHost}/static/@vite/client"></script>
|
||||||
<script type="module" nonce="${ctx.state.cspNonce}" src="http://localhost:3001/static/${entry}"></script>
|
<script type="module" nonce="${ctx.state.cspNonce}" src="${viteHost}/static/${entry}"></script>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
ctx.body = page
|
ctx.body = page
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
|
||||||
|
const exec = require("child_process").execSync;
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const sslDir = path.join(__dirname, "..", "config", "certs");
|
||||||
|
const sslCert = path.join(sslDir, "public.cert");
|
||||||
|
const sslKey = path.join(sslDir, "private.key");
|
||||||
|
|
||||||
|
if (!fs.existsSync(sslKey) || !fs.existsSync(sslCert)) {
|
||||||
|
try {
|
||||||
|
exec(
|
||||||
|
`mkcert -cert-file ${sslDir}/public.cert -key-file ${sslDir}/private.key "*.outline.dev" && mkcert -install`
|
||||||
|
);
|
||||||
|
console.log("🔒 Local SSL certificate created");
|
||||||
|
} catch (e) {
|
||||||
|
console.log(
|
||||||
|
"SSL certificates could not be generated. Ensure mkcert is installed and in your PATH"
|
||||||
|
);
|
||||||
|
console.log(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-2
@@ -25,13 +25,15 @@ export function getSSLOptions() {
|
|||||||
? Buffer.from(env.SSL_KEY, "base64").toString("ascii")
|
? Buffer.from(env.SSL_KEY, "base64").toString("ascii")
|
||||||
: undefined) ||
|
: undefined) ||
|
||||||
safeReadFile("private.key") ||
|
safeReadFile("private.key") ||
|
||||||
safeReadFile("private.pem"),
|
safeReadFile("private.pem") ||
|
||||||
|
safeReadFile("server/config/certs/private.key"),
|
||||||
cert:
|
cert:
|
||||||
(env.SSL_CERT
|
(env.SSL_CERT
|
||||||
? Buffer.from(env.SSL_CERT, "base64").toString("ascii")
|
? Buffer.from(env.SSL_CERT, "base64").toString("ascii")
|
||||||
: undefined) ||
|
: undefined) ||
|
||||||
safeReadFile("public.cert") ||
|
safeReadFile("public.cert") ||
|
||||||
safeReadFile("public.pem"),
|
safeReadFile("public.pem") ||
|
||||||
|
safeReadFile("server/config/certs/public.cert"),
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
+17
-1
@@ -1,3 +1,4 @@
|
|||||||
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import { optimizeLodashImports } from "@optimize-lodash/rollup-plugin";
|
import { optimizeLodashImports } from "@optimize-lodash/rollup-plugin";
|
||||||
@@ -5,7 +6,7 @@ import react from "@vitejs/plugin-react";
|
|||||||
import browserslistToEsbuild from "browserslist-to-esbuild";
|
import browserslistToEsbuild from "browserslist-to-esbuild";
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import { webpackStats } from "rollup-plugin-webpack-stats";
|
import { webpackStats } from "rollup-plugin-webpack-stats";
|
||||||
import { defineConfig } from "vite";
|
import { CommonServerOptions, defineConfig } from "vite";
|
||||||
import { VitePWA } from "vite-plugin-pwa";
|
import { VitePWA } from "vite-plugin-pwa";
|
||||||
import { viteStaticCopy } from "vite-plugin-static-copy";
|
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||||
|
|
||||||
@@ -14,6 +15,20 @@ dotenv.config({
|
|||||||
silent: true,
|
silent: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let httpsConfig: CommonServerOptions["https"] | undefined;
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
try {
|
||||||
|
httpsConfig = {
|
||||||
|
key: fs.readFileSync("./server/config/certs/private.key"),
|
||||||
|
cert: fs.readFileSync("./server/config/certs/public.cert"),
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn("No local SSL certs found, HTTPS will not be available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default () =>
|
export default () =>
|
||||||
defineConfig({
|
defineConfig({
|
||||||
root: "./",
|
root: "./",
|
||||||
@@ -22,6 +37,7 @@ export default () =>
|
|||||||
server: {
|
server: {
|
||||||
port: 3001,
|
port: 3001,
|
||||||
host: true,
|
host: true,
|
||||||
|
https: httpsConfig,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
// https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#readme
|
// https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-react#readme
|
||||||
|
|||||||
Reference in New Issue
Block a user