Compare commits
8 Commits
14df74d776
...
dafa4242dd
| Author | SHA1 | Date | |
|---|---|---|---|
| dafa4242dd | |||
| b36274db37 | |||
| dc0550b112 | |||
|
|
1ceb87515d | ||
|
|
1c4817486b | ||
|
|
4b1b87abde | ||
|
|
5e841f6b16 | ||
|
|
6fd7e755b0 |
46
.github/workflows/publish-docker.yml
vendored
Normal file
46
.github/workflows/publish-docker.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: CI/CD Pipeline
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# - name: Setup Node.js environment
|
||||||
|
# uses: actions/setup-node@v4
|
||||||
|
# with:
|
||||||
|
# node-version: "18"
|
||||||
|
|
||||||
|
# - name: Install Dependencies
|
||||||
|
# run: yarn install
|
||||||
|
|
||||||
|
# - name: Build Project
|
||||||
|
# run: yarn build
|
||||||
|
|
||||||
|
- name: Docker login
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: Dockerfile.base
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
yuuza/outline-base:${{ github.ref_name == 'main' && 'latest' || github.ref_name }}
|
||||||
|
cache-from: type=registry,ref=yuuza/outline-base:${{ github.ref_name == 'main' && 'latest' || github.ref_name }}
|
||||||
|
cache-to: type=inline
|
||||||
|
- run: docker tag yuuza/outline-base:${{ github.ref_name == 'main' && 'latest' || github.ref_name }} outlinewiki/outline-base:latest
|
||||||
|
- name: Build runner image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
yuuza/outline:${{ github.ref_name == 'main' && 'latest' || github.ref_name }}
|
||||||
|
cache-from: type=registry,ref=yuuza/outline:${{ github.ref_name == 'main' && 'latest' || github.ref_name }}
|
||||||
|
cache-to: type=inline
|
||||||
@@ -81,6 +81,13 @@ function DocumentListItem(
|
|||||||
title: document.titleWithDefault,
|
title: document.titleWithDefault,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
if (menuOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
handleMenuOpen();
|
||||||
|
}}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<Content>
|
<Content>
|
||||||
@@ -137,6 +144,7 @@ function DocumentListItem(
|
|||||||
<DocumentMenu
|
<DocumentMenu
|
||||||
document={document}
|
document={document}
|
||||||
showPin={showPin}
|
showPin={showPin}
|
||||||
|
visible={menuOpen}
|
||||||
onOpen={handleMenuOpen}
|
onOpen={handleMenuOpen}
|
||||||
onClose={handleMenuClose}
|
onClose={handleMenuClose}
|
||||||
modal={false}
|
modal={false}
|
||||||
|
|||||||
@@ -146,18 +146,20 @@ function CollectionMemberList({ collection, invitedInSession }: Props) {
|
|||||||
<InputMemberPermissionSelect
|
<InputMemberPermissionSelect
|
||||||
style={{ margin: 0 }}
|
style={{ margin: 0 }}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
onChange={async (permission: CollectionPermission) => {
|
onChange={async (
|
||||||
if (permission) {
|
permission: CollectionPermission | typeof EmptySelectValue
|
||||||
|
) => {
|
||||||
|
if (permission === EmptySelectValue) {
|
||||||
|
await memberships.delete({
|
||||||
|
collectionId: collection.id,
|
||||||
|
userId: membership.userId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
await memberships.create({
|
await memberships.create({
|
||||||
collectionId: collection.id,
|
collectionId: collection.id,
|
||||||
userId: membership.userId,
|
userId: membership.userId,
|
||||||
permission,
|
permission,
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
await memberships.delete({
|
|
||||||
collectionId: collection.id,
|
|
||||||
userId: membership.userId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
disabled={!can.update}
|
disabled={!can.update}
|
||||||
|
|||||||
@@ -131,7 +131,16 @@ const CollectionLink: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Relative ref={drop}>
|
<Relative
|
||||||
|
ref={drop}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
if (menuOpen || isEditing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
handleMenuOpen();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<DropToImport collectionId={collection.id}>
|
<DropToImport collectionId={collection.id}>
|
||||||
<SidebarLink
|
<SidebarLink
|
||||||
to={{
|
to={{
|
||||||
@@ -178,6 +187,7 @@ const CollectionLink: React.FC<Props> = ({
|
|||||||
onRename={() =>
|
onRename={() =>
|
||||||
editableTitleRef.current?.setIsEditing(true)
|
editableTitleRef.current?.setIsEditing(true)
|
||||||
}
|
}
|
||||||
|
visible={menuOpen}
|
||||||
onOpen={handleMenuOpen}
|
onOpen={handleMenuOpen}
|
||||||
onClose={handleMenuClose}
|
onClose={handleMenuClose}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -310,6 +310,13 @@ function InnerDocumentLink(
|
|||||||
$isDragging={isDragging}
|
$isDragging={isDragging}
|
||||||
$isMoving={isMoving}
|
$isMoving={isMoving}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
if (menuOpen || isEditing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
handleMenuOpen();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div ref={dropToReparent}>
|
<div ref={dropToReparent}>
|
||||||
<DropToImport documentId={node.id} activeClassName="activeDropZone">
|
<DropToImport documentId={node.id} activeClassName="activeDropZone">
|
||||||
@@ -372,6 +379,7 @@ function InnerDocumentLink(
|
|||||||
onRename={() =>
|
onRename={() =>
|
||||||
editableTitleRef.current?.setIsEditing(true)
|
editableTitleRef.current?.setIsEditing(true)
|
||||||
}
|
}
|
||||||
|
visible={menuOpen}
|
||||||
onOpen={handleMenuOpen}
|
onOpen={handleMenuOpen}
|
||||||
onClose={handleMenuClose}
|
onClose={handleMenuClose}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -115,7 +115,18 @@ function StarredLink({ star }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Draggable key={star.id} ref={draggableRef} $isDragging={isDragging}>
|
<Draggable
|
||||||
|
key={star.id}
|
||||||
|
ref={draggableRef}
|
||||||
|
$isDragging={isDragging}
|
||||||
|
onContextMenu={(e) => {
|
||||||
|
if (menuOpen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
handleMenuOpen();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<SidebarLink
|
<SidebarLink
|
||||||
depth={0}
|
depth={0}
|
||||||
to={{
|
to={{
|
||||||
@@ -136,6 +147,7 @@ function StarredLink({ star }: Props) {
|
|||||||
<Fade>
|
<Fade>
|
||||||
<DocumentMenu
|
<DocumentMenu
|
||||||
document={document}
|
document={document}
|
||||||
|
visible={menuOpen}
|
||||||
onOpen={handleMenuOpen}
|
onOpen={handleMenuOpen}
|
||||||
onClose={handleMenuClose}
|
onClose={handleMenuClose}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ type Props = {
|
|||||||
collection: Collection;
|
collection: Collection;
|
||||||
placement?: Placement;
|
placement?: Placement;
|
||||||
modal?: boolean;
|
modal?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
label?: (props: MenuButtonHTMLProps) => React.ReactNode;
|
label?: (props: MenuButtonHTMLProps) => React.ReactNode;
|
||||||
onRename?: () => void;
|
onRename?: () => void;
|
||||||
onOpen?: () => void;
|
onOpen?: () => void;
|
||||||
@@ -49,6 +50,7 @@ function CollectionMenu({
|
|||||||
collection,
|
collection,
|
||||||
label,
|
label,
|
||||||
modal = true,
|
modal = true,
|
||||||
|
visible,
|
||||||
placement,
|
placement,
|
||||||
onRename,
|
onRename,
|
||||||
onOpen,
|
onOpen,
|
||||||
@@ -64,6 +66,12 @@ function CollectionMenu({
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const file = React.useRef<HTMLInputElement>(null);
|
const file = React.useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (visible !== undefined && menu.visible !== visible) {
|
||||||
|
menu.setVisible(visible);
|
||||||
|
}
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
const handleExport = React.useCallback(() => {
|
const handleExport = React.useCallback(() => {
|
||||||
dialogs.openModal({
|
dialogs.openModal({
|
||||||
title: t("Export collection"),
|
title: t("Export collection"),
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ type Props = {
|
|||||||
/** Pass true if the document is currently being displayed */
|
/** Pass true if the document is currently being displayed */
|
||||||
showDisplayOptions?: boolean;
|
showDisplayOptions?: boolean;
|
||||||
modal?: boolean;
|
modal?: boolean;
|
||||||
|
visible?: boolean;
|
||||||
showToggleEmbeds?: boolean;
|
showToggleEmbeds?: boolean;
|
||||||
showPin?: boolean;
|
showPin?: boolean;
|
||||||
label?: (props: MenuButtonHTMLProps) => React.ReactNode;
|
label?: (props: MenuButtonHTMLProps) => React.ReactNode;
|
||||||
@@ -74,6 +75,7 @@ function DocumentMenu({
|
|||||||
document,
|
document,
|
||||||
className,
|
className,
|
||||||
modal = true,
|
modal = true,
|
||||||
|
visible,
|
||||||
showToggleEmbeds,
|
showToggleEmbeds,
|
||||||
showDisplayOptions,
|
showDisplayOptions,
|
||||||
label,
|
label,
|
||||||
@@ -106,6 +108,12 @@ function DocumentMenu({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (visible !== undefined && menu.visible !== visible) {
|
||||||
|
menu.setVisible(visible);
|
||||||
|
}
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
const handleOpen = React.useCallback(async () => {
|
const handleOpen = React.useCallback(async () => {
|
||||||
if (!data && !loading) {
|
if (!data && !loading) {
|
||||||
await request();
|
await request();
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class FeatureFlags {
|
|||||||
this.initalized = true;
|
this.initalized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.cache.has(flag) ?? FeatureDefaults[flag];
|
return this.cache.has(flag) ? true : FeatureDefaults[flag] ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static enable(flag: Feature) {
|
public static enable(flag: Feature) {
|
||||||
|
|||||||
@@ -210,7 +210,7 @@
|
|||||||
"resolve-path": "^1.4.0",
|
"resolve-path": "^1.4.0",
|
||||||
"rfc6902": "^5.1.1",
|
"rfc6902": "^5.1.1",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"semver": "^7.5.2",
|
"semver": "^7.6.2",
|
||||||
"sequelize": "^6.37.3",
|
"sequelize": "^6.37.3",
|
||||||
"sequelize-cli": "^6.6.1",
|
"sequelize-cli": "^6.6.1",
|
||||||
"sequelize-encrypted": "^1.0.0",
|
"sequelize-encrypted": "^1.0.0",
|
||||||
@@ -304,7 +304,7 @@
|
|||||||
"@types/redis-info": "^3.0.3",
|
"@types/redis-info": "^3.0.3",
|
||||||
"@types/refractor": "^3.4.0",
|
"@types/refractor": "^3.4.0",
|
||||||
"@types/resolve-path": "^1.4.2",
|
"@types/resolve-path": "^1.4.2",
|
||||||
"@types/semver": "^7.5.6",
|
"@types/semver": "^7.5.8",
|
||||||
"@types/sequelize": "^4.28.19",
|
"@types/sequelize": "^4.28.19",
|
||||||
"@types/slug": "^5.0.7",
|
"@types/slug": "^5.0.7",
|
||||||
"@types/stoppable": "^1.1.3",
|
"@types/stoppable": "^1.1.3",
|
||||||
|
|||||||
@@ -203,16 +203,32 @@ export class ProsemirrorHelper {
|
|||||||
|
|
||||||
const json = doc.toJSON() as ProsemirrorData;
|
const json = doc.toJSON() as ProsemirrorData;
|
||||||
|
|
||||||
|
function getMapping(href: string) {
|
||||||
|
let relativeHref;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(href);
|
||||||
|
relativeHref = url.toString().substring(url.origin.length);
|
||||||
|
} catch {
|
||||||
|
// Noop: Invalid url.
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
mapping[href] ||
|
||||||
|
(relativeHref ? mapping[relativeHref] : undefined) ||
|
||||||
|
href
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function replaceAttachmentUrls(node: ProsemirrorData) {
|
function replaceAttachmentUrls(node: ProsemirrorData) {
|
||||||
if (node.attrs?.src) {
|
if (node.attrs?.src) {
|
||||||
node.attrs.src = mapping[node.attrs.src as string] || node.attrs.src;
|
node.attrs.src = getMapping(String(node.attrs.src));
|
||||||
} else if (node.attrs?.href) {
|
} else if (node.attrs?.href) {
|
||||||
node.attrs.href = mapping[node.attrs.href as string] || node.attrs.href;
|
node.attrs.href = getMapping(String(node.attrs.href));
|
||||||
} else if (node.marks) {
|
} else if (node.marks) {
|
||||||
node.marks.forEach((mark) => {
|
node.marks.forEach((mark) => {
|
||||||
if (mark.attrs?.href) {
|
if (mark.attrs?.href) {
|
||||||
mark.attrs.href =
|
mark.attrs.href = getMapping(String(mark.attrs.href));
|
||||||
mapping[mark.attrs.href as string] || mark.attrs.href;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,8 +320,8 @@ width: 100%;
|
|||||||
}
|
}
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
margin-top: .5em;
|
margin-top: 0.85em;
|
||||||
margin-bottom: .5em;
|
margin-bottom: 0.85em;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@@ -497,8 +497,8 @@ iframe.embed {
|
|||||||
max-width: 840px;
|
max-width: 840px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
||||||
margin-top: 0.5em;
|
margin-top: 0.85em;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.85em;
|
||||||
|
|
||||||
.title,
|
.title,
|
||||||
.subtitle {
|
.subtitle {
|
||||||
@@ -1244,7 +1244,7 @@ pre {
|
|||||||
background: ${props.theme.codeBackground};
|
background: ${props.theme.codeBackground};
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid ${props.theme.codeBorder};
|
border: 1px solid ${props.theme.codeBorder};
|
||||||
margin: .5em 0;
|
margin: 0.85em 0;
|
||||||
|
|
||||||
-webkit-font-smoothing: initial;
|
-webkit-font-smoothing: initial;
|
||||||
font-family: ${props.theme.fontFamilyMono};
|
font-family: ${props.theme.fontFamilyMono};
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export default createGlobalStyle<Props>`
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
margin-bottom: 0.5em;
|
margin-bottom: 0.85em;
|
||||||
}
|
}
|
||||||
h1 { font-size: 36px; }
|
h1 { font-size: 36px; }
|
||||||
h2 { font-size: 26px; }
|
h2 { font-size: 26px; }
|
||||||
|
|||||||
18
yarn.lock
18
yarn.lock
@@ -4883,10 +4883,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
|
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
|
||||||
integrity "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk= sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
integrity "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk= sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
||||||
|
|
||||||
"@types/semver@^7.5.0", "@types/semver@^7.5.6":
|
"@types/semver@^7.5.0", "@types/semver@^7.5.8":
|
||||||
version "7.5.6"
|
version "7.5.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339"
|
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
|
||||||
integrity "sha1-xlsr/OG+w0ZYLAdyTj+MEBeiAzk= sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A=="
|
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
|
||||||
|
|
||||||
"@types/sequelize@^4.28.19":
|
"@types/sequelize@^4.28.19":
|
||||||
version "4.28.19"
|
version "4.28.19"
|
||||||
@@ -13416,12 +13416,10 @@ semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semve
|
|||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||||
|
|
||||||
semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
|
semver@^7.5.0, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.2:
|
||||||
version "7.6.0"
|
version "7.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
|
||||||
integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
|
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
|
||||||
dependencies:
|
|
||||||
lru-cache "^6.0.0"
|
|
||||||
|
|
||||||
semver@~7.0.0:
|
semver@~7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user