diff --git a/.dockerignore b/.dockerignore
index 2a4142088908e..20e2758d3dbe6 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -78,7 +78,7 @@ cpu.out
/public/js
/public/css
/public/fonts
-/public/img/webpack
+/public/img/bundled
/vendor
/web_src/fomantic/node_modules
/web_src/fomantic/build/*
diff --git a/.gitignore b/.gitignore
index 6851be742c641..a2a4011ef3d9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,7 +75,7 @@ cpu.out
/public/js
/public/css
/public/fonts
-/public/img/webpack
+/public/img/bundled
/vendor
/web_src/fomantic/node_modules
/web_src/fomantic/build/*
diff --git a/Makefile b/Makefile
index 7de96f09fdd18..9eaa69537782c 100644
--- a/Makefile
+++ b/Makefile
@@ -114,10 +114,10 @@ GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/m
FOMANTIC_WORK_DIR := web_src/fomantic
-WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
-WEBPACK_CONFIGS := webpack.config.js
-WEBPACK_DEST := public/js/index.js public/css/index.css
-WEBPACK_DEST_ENTRIES := public/js public/css public/fonts public/img/webpack
+VITE_SOURCES := $(shell find web_src/js web_src/css -type f)
+VITE_CONFIGS := vite.config.js
+VITE_DEST := public/js/index.js public/css/index.css
+VITE_DEST_ENTRIES := public/js public/css public/fonts public/img/bundled
BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
@@ -228,7 +228,7 @@ help:
@echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
@echo " - update-js update js dependencies"
@echo " - update-py update py dependencies"
- @echo " - webpack build webpack files"
+ @echo " - vite build vite files"
@echo " - svg build svg files"
@echo " - fomantic build fomantic files"
@echo " - generate run \"go generate\""
@@ -273,7 +273,7 @@ node-check:
.PHONY: clean-all
clean-all: clean
- rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
+ rm -rf $(VITE_DEST_ENTRIES) node_modules
.PHONY: clean
clean:
@@ -434,8 +434,8 @@ watch:
.PHONY: watch-frontend
watch-frontend: node-check node_modules
- @rm -rf $(WEBPACK_DEST_ENTRIES)
- NODE_ENV=development npx webpack --watch --progress
+ @rm -rf $(VITE_DEST_ENTRIES)
+ NODE_ENV=development npx vite
.PHONY: watch-backend
watch-backend: go-check
@@ -806,7 +806,7 @@ install: $(wildcard *.go)
build: frontend backend
.PHONY: frontend
-frontend: $(WEBPACK_DEST)
+frontend: $(VITE_DEST)
.PHONY: backend
backend: go-check generate-backend $(EXECUTABLE)
@@ -952,14 +952,14 @@ fomantic:
$(SED_INPLACE) -e 's/\r//g' $(FOMANTIC_WORK_DIR)/build/semantic.css $(FOMANTIC_WORK_DIR)/build/semantic.js
rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
-.PHONY: webpack
-webpack: $(WEBPACK_DEST)
+.PHONY: vite
+vite: $(VITE_DEST)
-$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
+$(VITE_DEST): $(VITE_SOURCES) $(VITE_CONFIGS) package-lock.json
@$(MAKE) -s node-check node_modules
- rm -rf $(WEBPACK_DEST_ENTRIES)
- npx webpack
- @touch $(WEBPACK_DEST)
+ rm -rf $(VITE_DEST_ENTRIES)
+ npx vite build
+ @touch $(VITE_DEST)
.PHONY: svg
svg: node-check | node_modules
diff --git a/modules/httplib/proxy.go b/modules/httplib/proxy.go
new file mode 100644
index 0000000000000..b00f795bd3ba2
--- /dev/null
+++ b/modules/httplib/proxy.go
@@ -0,0 +1,42 @@
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package httplib
+
+import (
+ "crypto/tls"
+ "net/http"
+ "net/http/httputil"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// MakeReverseProxyHandler creates HTTP handler that reverse-proxies requests to destURL
+func MakeReverseProxyHandler(destURL, path string, skipVerify bool) http.Handler {
+ url, _ := url.Parse(destURL)
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ proxy := httputil.ReverseProxy{
+ Director: func(req *http.Request) {
+ req.URL.Scheme = url.Scheme
+ req.URL.Host = url.Host
+ req.URL.Path = url.Path + strings.TrimPrefix(req.URL.Path, path)
+ },
+ Transport: &http.Transport{
+ MaxIdleConns: 5,
+ IdleConnTimeout: 5 * time.Second,
+ TLSHandshakeTimeout: 5 * time.Second,
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: skipVerify,
+ },
+ },
+ ErrorHandler: func(rw http.ResponseWriter, r *http.Request, err error) {
+ rw.Header().Set("Content-Type", "text/plain")
+ _, _ = rw.Write([]byte(`502 Bad Gateway`))
+ rw.WriteHeader(http.StatusBadGateway)
+ },
+ }
+ proxy.ServeHTTP(w, r)
+ })
+}
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 2b918f42c099e..7b181da272e47 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -100,6 +100,9 @@ func NewFuncMap() template.FuncMap {
"AppDomain": func() string { // documented in mail-templates.md
return setting.Domain
},
+ "RunMode": func() string {
+ return setting.RunMode
+ },
"AssetVersion": func() string {
return setting.AssetVersion
},
diff --git a/modules/web/middleware/data.go b/modules/web/middleware/data.go
index 9497dc02e23d3..fb6789de62d55 100644
--- a/modules/web/middleware/data.go
+++ b/modules/web/middleware/data.go
@@ -59,7 +59,5 @@ func CommonTemplateContextData() ContextData {
"EnableSwagger": setting.API.EnableSwagger,
"EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn,
"PageStartTime": time.Now(),
-
- "RunModeIsProd": setting.IsProd,
}
}
diff --git a/package-lock.json b/package-lock.json
index 92e4087b08495..9d1c2545c9f3d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"@github/text-expander-element": "2.5.0",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.4.0",
+ "@vitejs/plugin-vue": "4.2.3",
"@webcomponents/custom-elements": "1.6.0",
"add-asset-webpack-plugin": "2.0.1",
"ansi_up": "5.2.1",
@@ -38,6 +39,7 @@
"monaco-editor-webpack-plugin": "7.0.1",
"pdfobject": "2.2.12",
"pretty-ms": "8.0.0",
+ "rollup-plugin-license": "3.0.1",
"sortablejs": "1.15.0",
"swagger-ui-dist": "5.1.0",
"throttle-debounce": "5.0.0",
@@ -46,6 +48,8 @@
"toastify-js": "1.12.0",
"tributejs": "5.1.3",
"uint8-to-base64": "0.2.0",
+ "vite": "4.4.3",
+ "vite-plugin-monaco-editor": "1.1.0",
"vue": "3.3.4",
"vue-bar-graph": "2.0.0",
"vue-loader": "17.2.2",
@@ -58,7 +62,6 @@
"@eslint-community/eslint-plugin-eslint-comments": "3.2.1",
"@playwright/test": "1.35.1",
"@stoplight/spectral-cli": "6.8.0",
- "@vitejs/plugin-vue": "4.2.3",
"eslint": "8.44.0",
"eslint-plugin-array-func": "3.1.8",
"eslint-plugin-custom-elements": "0.0.8",
@@ -1921,7 +1924,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
"integrity": "sha512-R6JDUfiZbJA9cMiguQ7jxALsgiprjBeHL5ikpXfJCH62pPHtI+JdJ5xWj6Ev73yXSlYl86+blXn1kZHQ7uElxw==",
- "dev": true,
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
@@ -3150,6 +3152,11 @@
"node": ">= 12.0.0"
}
},
+ "node_modules/commenting": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz",
+ "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA=="
+ },
"node_modules/commondir": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
@@ -5247,7 +5254,6 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@@ -7890,6 +7896,17 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/mlly": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz",
@@ -7902,6 +7919,14 @@
"ufo": "^1.1.2"
}
},
+ "node_modules/moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/monaco-editor": {
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.40.0.tgz",
@@ -8289,6 +8314,17 @@
"node": ">= 8"
}
},
+ "node_modules/package-name-regex": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-2.0.6.tgz",
+ "integrity": "sha512-gFL35q7kbE/zBaPA3UKhp2vSzcPYx2ecbYuwv1ucE9Il6IIgBDweBlH8D68UFGZic2MkllKa2KHCfC1IQBQUYA==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/dword-design"
+ }
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -9234,7 +9270,6 @@
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
- "dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@@ -9245,6 +9280,39 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/rollup-plugin-license": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-3.0.1.tgz",
+ "integrity": "sha512-/lec6Y94Y3wMfTDeYTO/jSXII0GQ/XkDZCiqkMKxyU5D5nGPaxr/2JNYvAgYsoCYuOLGOanKDPjCCQiTT96p7A==",
+ "dependencies": {
+ "commenting": "~1.1.0",
+ "glob": "~7.2.0",
+ "lodash": "~4.17.21",
+ "magic-string": "~0.26.2",
+ "mkdirp": "~1.0.4",
+ "moment": "~2.29.3",
+ "package-name-regex": "~2.0.6",
+ "spdx-expression-validate": "~2.0.0",
+ "spdx-satisfies": "~5.0.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.0.0 || ^2.0.0 || ^3.0.0"
+ }
+ },
+ "node_modules/rollup-plugin-license/node_modules/magic-string": {
+ "version": "0.26.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
+ "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
+ "dependencies": {
+ "sourcemap-codec": "^1.4.8"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/rrweb-cssom": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
@@ -9651,8 +9719,7 @@
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
- "deprecated": "Please use @jridgewell/sourcemap-codec instead",
- "dev": true
+ "deprecated": "Please use @jridgewell/sourcemap-codec instead"
},
"node_modules/spdx-compare": {
"version": "1.0.0",
@@ -10648,13 +10715,12 @@
}
},
"node_modules/vite": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.2.tgz",
- "integrity": "sha512-zUcsJN+UvdSyHhYa277UHhiJ3iq4hUBwHavOpsNUGsTgjBeoBlK8eDt+iT09pBq0h9/knhG/SPrZiM7cGmg7NA==",
- "dev": true,
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.3.tgz",
+ "integrity": "sha512-IMnXQXXWgLi5brBQx/4WzDxdzW0X3pjO4nqFJAuNvwKtxzAmPzFE1wszW3VDpAGQJm3RZkm/brzRdyGsnwgJIA==",
"dependencies": {
"esbuild": "^0.18.10",
- "postcss": "^8.4.24",
+ "postcss": "^8.4.25",
"rollup": "^3.25.2"
},
"bin": {
@@ -10725,6 +10791,14 @@
"url": "https://opencollective.com/vitest"
}
},
+ "node_modules/vite-plugin-monaco-editor": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz",
+ "integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==",
+ "peerDependencies": {
+ "monaco-editor": ">=0.33.0"
+ }
+ },
"node_modules/vite-string-plugin": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.1.0.tgz",
@@ -10741,7 +10815,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -10757,7 +10830,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -10773,7 +10845,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"android"
@@ -10789,7 +10860,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -10805,7 +10875,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"darwin"
@@ -10821,7 +10890,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -10837,7 +10905,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"freebsd"
@@ -10853,7 +10920,6 @@
"cpu": [
"arm"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10869,7 +10935,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10885,7 +10950,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10901,7 +10965,6 @@
"cpu": [
"loong64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10917,7 +10980,6 @@
"cpu": [
"mips64el"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10933,7 +10995,6 @@
"cpu": [
"ppc64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10949,7 +11010,6 @@
"cpu": [
"riscv64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10965,7 +11025,6 @@
"cpu": [
"s390x"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10981,7 +11040,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"linux"
@@ -10997,7 +11055,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"netbsd"
@@ -11013,7 +11070,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"openbsd"
@@ -11029,7 +11085,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"sunos"
@@ -11045,7 +11100,6 @@
"cpu": [
"arm64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -11061,7 +11115,6 @@
"cpu": [
"ia32"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -11077,7 +11130,6 @@
"cpu": [
"x64"
],
- "dev": true,
"optional": true,
"os": [
"win32"
@@ -11090,7 +11142,6 @@
"version": "0.18.11",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.11.tgz",
"integrity": "sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==",
- "dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@@ -11127,7 +11178,6 @@
"version": "3.26.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz",
"integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==",
- "dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
diff --git a/package.json b/package.json
index 10956595b6891..f7e49fbdd0a97 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"@github/text-expander-element": "2.5.0",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.4.0",
+ "@vitejs/plugin-vue": "4.2.3",
"@webcomponents/custom-elements": "1.6.0",
"add-asset-webpack-plugin": "2.0.1",
"ansi_up": "5.2.1",
@@ -37,6 +38,7 @@
"monaco-editor-webpack-plugin": "7.0.1",
"pdfobject": "2.2.12",
"pretty-ms": "8.0.0",
+ "rollup-plugin-license": "3.0.1",
"sortablejs": "1.15.0",
"swagger-ui-dist": "5.1.0",
"throttle-debounce": "5.0.0",
@@ -45,6 +47,8 @@
"toastify-js": "1.12.0",
"tributejs": "5.1.3",
"uint8-to-base64": "0.2.0",
+ "vite": "4.4.3",
+ "vite-plugin-monaco-editor": "1.1.0",
"vue": "3.3.4",
"vue-bar-graph": "2.0.0",
"vue-loader": "17.2.2",
@@ -57,7 +61,6 @@
"@eslint-community/eslint-plugin-eslint-comments": "3.2.1",
"@playwright/test": "1.35.1",
"@stoplight/spectral-cli": "6.8.0",
- "@vitejs/plugin-vue": "4.2.3",
"eslint": "8.44.0",
"eslint-plugin-array-func": "3.1.8",
"eslint-plugin-custom-elements": "0.0.8",
diff --git a/routers/web/web.go b/routers/web/web.go
index 4f5901c0ec661..5a221266f419e 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -6,10 +6,12 @@ package web
import (
gocontext "context"
"net/http"
+ "os"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/metrics"
"code.gitea.io/gitea/modules/public"
@@ -108,7 +110,22 @@ func Routes() *web.Route {
routes := web.NewRoute()
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
- routes.RouteMethods("/assets/*", "GET, HEAD", CorsHandler(), public.AssetsHandlerFunc("/assets/"))
+
+ if setting.IsProd { // use assets from embed or filesystem
+ routes.RouteMethods("/assets/*", "GET, HEAD", CorsHandler(), public.AssetsHandlerFunc("/assets/"))
+ } else { // reverse-proxy asset requests to webpack-dev-server
+ // this environment variable is intentionally not loaded from ini config
+ // because webpack also needs to parse it from the environment
+ port := os.Getenv("GITEA_DEV_FRONTEND_PORT")
+ if port == "" {
+ port = "3001"
+ }
+
+ routes.RouteMethods("/assets/*", "GET, HEAD", CorsHandler(),
+ httplib.MakeReverseProxyHandler("http://localhost:"+port, "/assets", true),
+ )
+ }
+
routes.RouteMethods("/avatars/*", "GET, HEAD", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
routes.RouteMethods("/repo-avatars/*", "GET, HEAD", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
routes.RouteMethods("/apple-touch-icon.png", "GET, HEAD", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl
index e3cac806a4d91..b1d4351fcc4f6 100644
--- a/templates/base/footer.tmpl
+++ b/templates/base/footer.tmpl
@@ -25,7 +25,16 @@
{{end}}
{{end}}
-
+ {{$prefix := ""}}
+ {{if eq RunMode "dev"}}
+ {{$prefix = "http://localhost:3001"}}
+ {{else}}
+ {{$prefix = AssetUrlPrefix}}
+ {{end}}
+ {{if eq RunMode "dev"}}
+
+ {{end}}
+
{{template "custom/footer" .}}