fix: added BASE_URL support
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
|
||||
BASE_URL=/
|
||||
DATABASE_PATH=db.local.json
|
||||
@@ -6,6 +6,7 @@ node_modules
|
||||
dist
|
||||
|
||||
# Local files
|
||||
.env
|
||||
*.local*
|
||||
|
||||
# Editor directories and files
|
||||
|
||||
@@ -7,6 +7,7 @@ Sito con una bacheca di problemi e la possibilità di inviare soluzioni in markd
|
||||
Installare tutte le dipendenze con il package manager preferito di turno (ed in tal caso sostituire `npm` con ad esempio `pnpm` o `yarn`)
|
||||
|
||||
```bash shell
|
||||
$ cp .env.development .env
|
||||
$ npm install
|
||||
```
|
||||
|
||||
@@ -25,38 +26,4 @@ $ npm run serve
|
||||
|
||||
# TODO
|
||||
|
||||
- Pagina profilo utente
|
||||
- Lista soluzioni inviate (con stato delle soluzioni: approvate o rifiutate)
|
||||
|
||||
- Pagina dell'admin
|
||||
- Lista delle soluzioni non corrette
|
||||
- Creazione nuovi problemi
|
||||
|
||||
- DBv2
|
||||
|
||||
```js
|
||||
relations: [
|
||||
{
|
||||
from: 'Solution'
|
||||
name: 'for'
|
||||
to: 'Problem'
|
||||
entries: [
|
||||
['LRLAH2NoLFQAQHQgz', '1']
|
||||
['JzyiDnwRCrkpzLL8W', '1']
|
||||
['FFYMJjP2yr4ohdmdT', '2']
|
||||
['VFHTb8fSrLOkPNVFx', '2']
|
||||
]
|
||||
}
|
||||
{
|
||||
from: 'User'
|
||||
name: 'owns'
|
||||
to: 'Solution'
|
||||
entries: [
|
||||
['aziis98', 'LRLAH2NoLFQAQHQgz']
|
||||
['aziis98', 'JzyiDnwRCrkpzLL8W']
|
||||
['BachoSeven', 'FFYMJjP2yr4ohdmdT']
|
||||
['BachoSeven', 'VFHTb8fSrLOkPNVFx']
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
- [ ] Autenticazione vera magari con OAuth dell'ateneo o Google
|
||||
+7
-3
@@ -1,10 +1,14 @@
|
||||
export function prependBaseUrl(route: string) {
|
||||
return import.meta.env.BASE_URL + (route.startsWith('/') ? route.substring(1) : route)
|
||||
}
|
||||
|
||||
export const server = {
|
||||
async get(url: string) {
|
||||
const res = await fetch(url, { credentials: 'include' })
|
||||
const res = await fetch(prependBaseUrl(url), { credentials: 'include' })
|
||||
return await res.json()
|
||||
},
|
||||
async post<T>(url: string, body?: T) {
|
||||
const res = await fetch(url, {
|
||||
const res = await fetch(prependBaseUrl(url), {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
@@ -16,7 +20,7 @@ export const server = {
|
||||
return await res.json()
|
||||
},
|
||||
async patch<T>(url: string, body?: T) {
|
||||
const res = await fetch(url, {
|
||||
const res = await fetch(prependBaseUrl(url), {
|
||||
method: 'PATCH',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Link } from 'preact-router/match'
|
||||
import { isAdministrator, User, UserRole } from '../../shared/model'
|
||||
import { prependBaseUrl } from '../api'
|
||||
|
||||
const ROLE_LABEL: Record<UserRole, string> = {
|
||||
['admin']: 'Admin',
|
||||
@@ -15,7 +16,7 @@ type Props = {
|
||||
export const Header = ({ user, noLogin }: Props) => (
|
||||
<header>
|
||||
<div class="logo">
|
||||
<a href="/">PHC / Problemi</a>
|
||||
<a href={prependBaseUrl('/')}>PHC / Problemi</a>
|
||||
</div>
|
||||
<nav>
|
||||
{user ? (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { prependBaseUrl } from '../api'
|
||||
import { Markdown } from './Markdown'
|
||||
|
||||
type Props = {
|
||||
@@ -11,7 +12,7 @@ export const Problem = ({ id, content, solutionsCount }: Props) => {
|
||||
<div class="problem">
|
||||
<div class="problem-header">
|
||||
<div class="problem-title">
|
||||
<a href={`/problem/${id}`}>Problema {id}</a>
|
||||
<a href={prependBaseUrl(`/problem/${id}`)}>Problema {id}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="problem-content">
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
SolutionStatus,
|
||||
UserId,
|
||||
} from '../../shared/model'
|
||||
import { server } from '../api'
|
||||
import { prependBaseUrl, server } from '../api'
|
||||
import { Markdown } from './Markdown'
|
||||
import { Select } from './Select'
|
||||
|
||||
@@ -89,13 +89,14 @@ export const Solution = ({
|
||||
{sentBy && (
|
||||
<>
|
||||
{' '}
|
||||
di <a href={`/user/${sentBy}`}>@{sentBy}</a>
|
||||
di <a href={prependBaseUrl(`/user/${sentBy}`)}>@{sentBy}</a>
|
||||
</>
|
||||
)}
|
||||
{forProblem && (
|
||||
<>
|
||||
{' '}
|
||||
per il <a href={`/problem/${forProblem}`}>Problema {forProblem}</a>
|
||||
per il{' '}
|
||||
<a href={prependBaseUrl(`/problem/${forProblem}`)}>Problema {forProblem}</a>
|
||||
</>
|
||||
)}
|
||||
{!isNaN(d as any) && (
|
||||
|
||||
+4
-5
@@ -1,7 +1,7 @@
|
||||
import { StateUpdater, useEffect, useState } from 'preact/hooks'
|
||||
|
||||
import { createContext } from 'preact'
|
||||
import { server } from './api'
|
||||
import { prependBaseUrl, server } from './api'
|
||||
import { User } from '../shared/model'
|
||||
|
||||
type Metadata = {
|
||||
@@ -13,9 +13,7 @@ export const MetadataContext = createContext<Metadata>({})
|
||||
export const ServerContext = createContext<boolean>(false)
|
||||
export const ClientContext = createContext<boolean>(false)
|
||||
|
||||
type CurrentUserHook = (
|
||||
onLoaded?: (user: User | null) => void
|
||||
) => [User | null, () => Promise<void>]
|
||||
type CurrentUserHook = (onLoaded?: (user: User | null) => void) => [User | null, () => Promise<void>]
|
||||
|
||||
export const useCurrentUser: CurrentUserHook = onLoaded => {
|
||||
const [user, setUser] = useState(null)
|
||||
@@ -49,7 +47,8 @@ export const useResource: ResourceHookFunction = (url, initialValue) => {
|
||||
function refresh() {
|
||||
const controller = new AbortController()
|
||||
const realUrl = typeof url === 'function' ? url() : url
|
||||
fetch(realUrl, { signal: controller.signal })
|
||||
|
||||
fetch(prependBaseUrl(realUrl), { signal: controller.signal })
|
||||
.then(res => {
|
||||
if (res.ok) {
|
||||
return res.json()
|
||||
|
||||
@@ -7,7 +7,8 @@ export const LoginPage = () => {
|
||||
const [username, setUsername] = useState('')
|
||||
|
||||
const login = async () => {
|
||||
await fetch(`/api/login`, {
|
||||
// @ts-ignore
|
||||
await fetch(`${import.meta.env.BASE_URL}/api/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -30,9 +31,7 @@ export const LoginPage = () => {
|
||||
id="login-username"
|
||||
type="text"
|
||||
value={username}
|
||||
onInput={e =>
|
||||
setUsername(e.target instanceof HTMLInputElement ? e.target.value : '')
|
||||
}
|
||||
onInput={e => setUsername(e.target instanceof HTMLInputElement ? e.target.value : '')}
|
||||
onKeyDown={e => e.key === 'Enter' && login()}
|
||||
/>
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"body-parser": "^1.20.1",
|
||||
"chalk": "^5.1.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"http-status-codes": "^2.2.0",
|
||||
"katex": "^0.16.3",
|
||||
|
||||
Generated
+7
@@ -11,6 +11,7 @@ specifiers:
|
||||
chalk: ^5.1.2
|
||||
concurrently: ^7.5.0
|
||||
cookie-parser: ^1.4.6
|
||||
dotenv: ^16.0.3
|
||||
esbuild: ^0.15.13
|
||||
express: ^4.18.2
|
||||
http-status-codes: ^2.2.0
|
||||
@@ -38,6 +39,7 @@ dependencies:
|
||||
body-parser: 1.20.1
|
||||
chalk: 5.1.2
|
||||
cookie-parser: 1.4.6
|
||||
dotenv: 16.0.3
|
||||
express: 4.18.2
|
||||
http-status-codes: 2.2.0
|
||||
katex: 0.16.3
|
||||
@@ -935,6 +937,11 @@ packages:
|
||||
engines: {node: '>=0.3.1'}
|
||||
dev: false
|
||||
|
||||
/dotenv/16.0.3:
|
||||
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/ee-first/1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
dev: false
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs/promises'
|
||||
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
import express, { Handler, Router } from 'express'
|
||||
import morgan from 'morgan'
|
||||
|
||||
@@ -9,6 +11,9 @@ import { createServer as createViteServer } from 'vite'
|
||||
import { createApiRouter } from './server/routes'
|
||||
import { RenderFunction } from './shared/ssr'
|
||||
|
||||
// Load ".env"
|
||||
dotenv.config()
|
||||
|
||||
const HTML_ROUTES = ['/', '/login', '/problem/:id', '/admin', '/profile']
|
||||
|
||||
const config = {
|
||||
@@ -47,8 +52,7 @@ async function createDevRouter() {
|
||||
const transformedTemplate = await vite.transformIndexHtml(req.originalUrl, indexHtml)
|
||||
|
||||
// Load (to be bundled) entry point for server side rendering
|
||||
const render: RenderFunction = (await vite.ssrLoadModule('./client/entry-server.tsx'))
|
||||
.default
|
||||
const render: RenderFunction = (await vite.ssrLoadModule('./client/entry-server.tsx')).default
|
||||
|
||||
const { html, metadata } = render(req.originalUrl)
|
||||
|
||||
@@ -58,9 +62,7 @@ async function createDevRouter() {
|
||||
const fullUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`
|
||||
const metaTagsHtml =
|
||||
'' +
|
||||
(metadata.title
|
||||
? `<meta property="og:title" content="${metadata.title}" />\n`
|
||||
: '') +
|
||||
(metadata.title ? `<meta property="og:title" content="${metadata.title}" />\n` : '') +
|
||||
(metadata.description
|
||||
? `<meta property="og:description" content="${metadata.description}" />\n`
|
||||
: '') +
|
||||
@@ -91,10 +93,7 @@ async function createProductionRouter() {
|
||||
r.use('/', express.static('dist/entry-client'))
|
||||
|
||||
mountIndexHtmlRoutes(r, async (req, res) => {
|
||||
const transformedTemplate = await fs.readFile(
|
||||
path.resolve('./dist/entry-client/index.html'),
|
||||
'utf-8'
|
||||
)
|
||||
const transformedTemplate = await fs.readFile(path.resolve('./dist/entry-client/index.html'), 'utf-8')
|
||||
|
||||
const { html, metadata } = render(req.originalUrl)
|
||||
|
||||
|
||||
+3
-8
@@ -53,7 +53,7 @@ export async function createApiRouter() {
|
||||
},
|
||||
}
|
||||
|
||||
const db = createDatabase('./db.local.json', initialDatabaseValue)
|
||||
const db = createDatabase(process.env.DATABASE_PATH ?? './db.local.json', initialDatabaseValue)
|
||||
|
||||
async function getRequestUser(req: Request) {
|
||||
const userId = sessions.getUserForSession(req.cookies.sid)
|
||||
@@ -159,9 +159,7 @@ export async function createApiRouter() {
|
||||
let solutions = await getSolutions(db)
|
||||
// se l'utente non è loggato o se non è un amministratore allora mostra solo le soluzioni "visibili"
|
||||
if (!requestUser || !isAdministrator(requestUser.role)) {
|
||||
solutions = solutions.filter(
|
||||
s => s.visible || (requestUser && s.sentBy === requestUser.id)
|
||||
)
|
||||
solutions = solutions.filter(s => s.visible || (requestUser && s.sentBy === requestUser.id))
|
||||
}
|
||||
// filtra rispetto agli utenti
|
||||
if (queryUser !== null) {
|
||||
@@ -232,10 +230,7 @@ export async function createApiRouter() {
|
||||
return
|
||||
}
|
||||
// uno studente può modificare solo il campo "content"
|
||||
if (
|
||||
user.role === 'student' &&
|
||||
!validateObjectKeys<keyof SolutionModel>(req.body, ['content'])
|
||||
) {
|
||||
if (user.role === 'student' && !validateObjectKeys<keyof SolutionModel>(req.body, ['content'])) {
|
||||
res.status(StatusCodes.UNAUTHORIZED)
|
||||
res.send(`a student can only modify the field "content"`)
|
||||
return
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact"
|
||||
"jsxImportSource": "preact",
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": ["client", "shared"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
||||
+5
-2
@@ -1,7 +1,10 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import preactPlugin from '@preact/preset-vite'
|
||||
import preact from '@preact/preset-vite'
|
||||
|
||||
console.log(`[Config] BASE_URL = "${process.env.BASE_URL}"`)
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [preactPlugin()],
|
||||
base: process.env.BASE_URL,
|
||||
plugins: [preact()],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user