Merge branch 'forgejo-dev' into newfeat
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
commit
c57a75e36a
50 changed files with 2760 additions and 961 deletions
106
app/api/auth/session/route.ts
Normal file
106
app/api/auth/session/route.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// POST /api/auth/session (path สั้นของ POST /api/note/auth/session — โค้ดก้อนเดียวกัน
|
||||
// copy มา 2026-11-05) — แลก credential ของลิงก์ e-Court เป็น access_token
|
||||
// (ฉบับ token 2026-10-30 ตามคำขอผู้ใช้ "note ใช้แบบ add-in" — แทน GET e-court ที่
|
||||
// set cookie คู่ oacCase ซึ่งถอดแล้ว): body {t | ticket_data+HmacSha256, fileUrl?,
|
||||
// typeId?} → verify → ลงทะเบียนเอกสาร N8 แบบ trusted ฝั่ง server เลย (fileId จาก
|
||||
// fid ที่เซ็นมา — ไม่ต้องมี link proof อีก) → ออก login-token อายุ NOTE_SESSION_TTL
|
||||
// (8 ชม.) ให้ client เก็บ localStorage "note-auth"
|
||||
// · 400 body ผิด · 401 {error, reason: expired|invalid} · 500 secret ไม่ตั้ง
|
||||
// · audit log เฉพาะ userid (strip อักขระควบคุม) — ห้าม log token/query ดิบ
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { addinTicketSecret, verifyAddinTicket } from "@/lib/addin-ticket";
|
||||
import { issueDocverToken, verifyDocverLoginToken } from "@/lib/docver-token";
|
||||
import { registerExternalFile } from "@/lib/external-import";
|
||||
import { parseSessionExchangeBody } from "@/lib/note-auth";
|
||||
import { NOTE_SESSION_TTL } from "@/lib/note-token";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
function json(body: unknown, status = 200): NextResponse {
|
||||
return NextResponse.json(body, { status, headers: { "cache-control": "no-store" } });
|
||||
}
|
||||
|
||||
export async function POST(request: Request): Promise<Response> {
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = await request.json();
|
||||
} catch {
|
||||
raw = null;
|
||||
}
|
||||
const parsed = parseSessionExchangeBody(raw);
|
||||
if (!parsed.ok) return json({ error: parsed.error }, 400);
|
||||
|
||||
const secret = process.env.DOCVER_TOKEN_SECRET?.trim();
|
||||
if (secret === undefined || secret === "") {
|
||||
console.error("[sso] DOCVER_TOKEN_SECRET ยังไม่ถูกตั้งค่า");
|
||||
return json({ error: "ระบบยังไม่พร้อมให้เข้าสู่ระบบ" }, 500);
|
||||
}
|
||||
|
||||
let identity: { userid: string; username: string; fid: string };
|
||||
if (parsed.credential.kind === "jwt") {
|
||||
const verified = verifyDocverLoginToken(parsed.credential.token, secret);
|
||||
if (!verified.ok) {
|
||||
return json(
|
||||
{ error: "ลิงก์เข้าสู่ระบบหมดอายุหรือไม่ถูกต้อง", reason: verified.reason },
|
||||
401,
|
||||
);
|
||||
}
|
||||
identity = verified.claims;
|
||||
} else {
|
||||
// ค่า default "super_secret" ของ addinTicketSecret() เป็นที่รู้กันทั้ง repo/
|
||||
// .env.example — ใครก็ปลอม ticket ของ uid ใครก็ได้ จึงปฏิเสธใน production
|
||||
// (fail-closed — กลุ่ม docver/add-in อนุญาต default ตาม requirement อยู่แล้ว
|
||||
// คนละประตู) · **dev ยอมรับค่า default** เพื่อให้ทดสอบทาง ticket รอบเครื่องได้
|
||||
// โดยไม่ต้องตั้ง env ใหม่ — เครื่องมือ dev (make-login-link/make-ticket-docx/
|
||||
// dev-link) ก็ default ค่าเดียวกัน (review 2026-10-30 รอบสอง)
|
||||
if (process.env.NODE_ENV === "production" && addinTicketSecret() === "super_secret") {
|
||||
console.error("[sso] ticket secret ยังเป็นค่า default (ADDIN_TICKET_SECRET/SECRET) — ปฏิเสธทาง ticket");
|
||||
return json({ error: "ระบบยังไม่พร้อมให้เข้าสู่ระบบ" }, 500);
|
||||
}
|
||||
const verified = verifyAddinTicket(
|
||||
parsed.credential.ticketData,
|
||||
parsed.credential.hmacHex,
|
||||
addinTicketSecret(),
|
||||
);
|
||||
if (!verified.ok) {
|
||||
return json({ error: "ลิงก์เข้าสู่ระบบหมดอายุหรือไม่ถูกต้อง", reason: "invalid" }, 401);
|
||||
}
|
||||
identity = { userid: verified.ticket.uid, username: verified.ticket.uname, fid: verified.ticket.fid };
|
||||
}
|
||||
|
||||
// N8: ลงทะเบียนเอกสารตอนแลก token เลย — ฝั่ง server เชื่อ fid จาก credential
|
||||
// ที่ verify แล้วเท่านั้น (trusted) ส่วน ?fileUrl พิมพ์เองบนหน้าเว็บไม่มีทาง
|
||||
// เข้ามาถึงจุดนี้ · ล้ม = ยังออก token ให้ + fileError ให้หน้าแรกแสดง (คู่ขนาน
|
||||
// พฤติกรรมสมัย cookie: session ตั้งก่อน แล้วค่อยแจ้ง error ของไฟล์)
|
||||
let fileId: string | undefined;
|
||||
let fileError: string | undefined;
|
||||
if (parsed.fileUrl !== undefined) {
|
||||
const result = await registerExternalFile(parsed.fileUrl, identity.fid, parsed.typeId, {
|
||||
trusted: true,
|
||||
});
|
||||
if (result.ok) fileId = result.fileId;
|
||||
else fileError = result.error;
|
||||
}
|
||||
|
||||
const access_token = issueDocverToken(
|
||||
{ userid: identity.userid, username: identity.username, fid: identity.fid, template: null },
|
||||
secret,
|
||||
NOTE_SESSION_TTL,
|
||||
"login",
|
||||
);
|
||||
// audit: เฉพาะ userid + เวลา — ห้าม log token/query ดิบ · strip อักขระควบคุม
|
||||
// ก่อนพิมพ์ (claims ไม่จำกัด charset — กันฝัง \n ปลอมบรรทัด log)
|
||||
const auditId = identity.userid.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
|
||||
console.log(`[sso] e-Court login ok: userid=${auditId}`);
|
||||
return json({
|
||||
access_token,
|
||||
token_type: "Bearer",
|
||||
expires_in: NOTE_SESSION_TTL,
|
||||
userid: identity.userid,
|
||||
username: identity.username,
|
||||
...(fileId !== undefined ? { fileId } : {}),
|
||||
...(fileError !== undefined ? { fileError } : {}),
|
||||
});
|
||||
}
|
||||
113
app/api/auth/token/route.ts
Normal file
113
app/api/auth/token/route.ts
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
// PLAN-DOCVER.md §auth — POST /api/auth/token (path สั้นของ POST /api/documents/token
|
||||
// — โค้ดก้อนเดียวกัน copy มา 2026-11-05) ออก access_token ของกลุ่ม docver
|
||||
// ทางเข้า 2 ทาง (PLAN-ADDIN-AUTH.md §3 — header x-api-key เป็นตัวตั้ง):
|
||||
// 1) e-Court server-to-server: header "x-api-key" เทียบ env DOCVER_API_KEY
|
||||
// (fail closed เมื่อ env ไม่ตั้ง/ว่าง) + body claims ตรง ๆ (เดิมทุกอย่าง)
|
||||
// 2) add-in ใน Word: ไม่มี x-api-key + body {ticket_data, HmacSha256} — server
|
||||
// ตรวจ HMAC ด้วย env ฝั่ง server (ADDIN_TICKET_SECRET → SECRET →
|
||||
// "super_secret", lib/addin-ticket.ts) แล้วออก token จาก claims ของ ticket
|
||||
// (secret ตัวเซ็น JWT ไม่มีทางไปอยู่ใน bundle ของ add-in — client ไม่ตรวจ HMAC
|
||||
// เองแล้วตาม PLAN-SSO.md D13) · contract 2026-11-04: 400 = ฟอร์แม็ตพัง
|
||||
// (รวม missing) / 401 = ลายเซ็นไม่ถูก / 403 = ลายเซ็นถูกแต่ ticket หมดอายุ
|
||||
// (client ที่มี JWT เดิมของ fid นี้ใน session ใช้ต่อเอง — ไม่มีการออกซ้ำ
|
||||
// จากหลักฐาน DB อีกต่อไป เลิก requirement ขั้น 7 ฉบับ 2026-10-28)
|
||||
// ทั้งสองทางออก JWT HS256 เซ็นด้วย env DOCVER_TOKEN_SECRET อายุตาม env
|
||||
// DOCVER_TOKEN_TTL_SECONDS (default 604800 = 7 วัน — token อายุยาวแทนระบบ
|
||||
// refresh/renew) แล้วนำไปแนบ Authorization: Bearer กับ endpoint อื่นของกลุ่ม
|
||||
// (lib/docver-token.ts) · segment คงที่ "token" ไม่ชน [docId]
|
||||
// (Next ให้ static ชนะ) และ middleware ยกเว้น api/documents/* อยู่แล้ว
|
||||
import { docverError } from "@/lib/document-versioning";
|
||||
import {
|
||||
addinTicketSecret,
|
||||
parseTicketTokenBody,
|
||||
ticketToDocverClaims,
|
||||
verifyAddinTicketHmac,
|
||||
} from "@/lib/addin-ticket";
|
||||
import { isTicketExpired } from "@/utils/docver-ticket";
|
||||
import {
|
||||
issueDocverToken,
|
||||
parseTokenRequestBody,
|
||||
requireDocverApiKey,
|
||||
type DocverTokenClaims,
|
||||
} from "@/lib/docver-token";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** 401 ของทางเข้า ticket — ข้อความเดียวทุกสาเหตุ (HMAC ผิด/รูปผิด) กัน oracle · หมดอายุแยกเป็น 403 */
|
||||
const TICKET_DENIED = "ticket ไม่ถูกต้อง";
|
||||
/** 403 ของทางเข้า ticket — ลายเซ็นถูกแต่หมดอายุ (client มี JWT เดิมใน session ก็ใช้ต่อได้) */
|
||||
const TICKET_EXPIRED = "ticket ของ e-Court หมดอายุ";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
return request.headers.get("x-api-key") !== null
|
||||
? postWithApiKey(request)
|
||||
: postWithTicket(request);
|
||||
}
|
||||
|
||||
/** ทางเข้า 1 (เดิม) — e-Court server-to-server */
|
||||
async function postWithApiKey(request: Request): Promise<Response> {
|
||||
const apiKeyFail = requireDocverApiKey(request);
|
||||
if (apiKeyFail !== null) return apiKeyFail;
|
||||
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = await request.json();
|
||||
} catch {
|
||||
return docverError("body ต้องเป็น JSON", 400);
|
||||
}
|
||||
const parsed = parseTokenRequestBody(raw);
|
||||
if (!parsed.ok) return docverError(parsed.error, 400);
|
||||
return issueToken(parsed.claims);
|
||||
}
|
||||
|
||||
/** ทางเข้า 2 — add-in ส่ง ticket ที่ e-Court stamp มากับไฟล์ */
|
||||
async function postWithTicket(request: Request): Promise<Response> {
|
||||
let raw: unknown;
|
||||
try {
|
||||
raw = await request.json();
|
||||
} catch {
|
||||
return docverError("body ต้องเป็น JSON", 400);
|
||||
}
|
||||
const parsed = parseTicketTokenBody(raw);
|
||||
if (!parsed.ok) {
|
||||
// contract 2026-11-04: ฟอร์แม็ตพัง = 400 ทั้ง missing/invalid (เดิม missing เป็น 401)
|
||||
return docverError(parsed.error, 400);
|
||||
}
|
||||
// ตรวจ HMAC ก่อนเสมอ (ตัวตนของผู้ถือไฟล์) — exp ไม่ใช่เกณฑ์ตัดสิทธิ์ ณ จุดนี้
|
||||
const verified = verifyAddinTicketHmac(parsed.ticketData, parsed.hmacHex, addinTicketSecret());
|
||||
if (!verified.ok) return docverError(TICKET_DENIED, 401);
|
||||
|
||||
// ลายเซ็นถูกแต่ ticket หมดอายุ → 403 เสมอ (contract 2026-11-04 — เลิก requirement
|
||||
// ขั้น 7 ฉบับ 2026-10-28: ไม่ตรวจ hasVersionByUser อีกต่อไป เพราะ ticket ฝังอยู่ใน
|
||||
// ไฟล์ ใครได้ไฟล์ก็ได้ ticket การปล่อยให้ ticket เก่าแลก token ใหม่ได้ตลอดไป
|
||||
// เท่ากันคือปล่อยสิทธิ์ตามการครอบครองไฟล์ — ฝั่ง client ที่มี JWT ของ fid นี้
|
||||
// ใน session ที่ยังไม่หมด (7 วัน) ใช้ต่อเอง ไม่มี = ขอไฟล์ใหม่จาก e-Court)
|
||||
if (isTicketExpired(verified.ticket.exp)) {
|
||||
return docverError(TICKET_EXPIRED, 403);
|
||||
}
|
||||
return issueToken(ticketToDocverClaims(verified.ticket));
|
||||
}
|
||||
|
||||
/** ออก JWT ของ claims ที่ผ่านการยืนยันแล้วจากทั้งสองทาง — ส่วนกลาง */
|
||||
async function issueToken(claims: DocverTokenClaims): Promise<Response> {
|
||||
// ความผิด config ฝั่ง server ไม่ใช่ความผิดของผู้เรียก — ไม่ตั้ง = 500 (ห้าม log ค่า secret)
|
||||
const secret = process.env.DOCVER_TOKEN_SECRET?.trim();
|
||||
if (secret === undefined || secret === "") {
|
||||
return docverError("DOCVER_TOKEN_SECRET ยังไม่ถูกตั้งค่า", 500);
|
||||
}
|
||||
const ttlRaw = Number(process.env.DOCVER_TOKEN_TTL_SECONDS);
|
||||
// default 7 วัน (2026-11-04) — token อายุยาวแทนระบบ refresh/renew ของ add-in
|
||||
const ttlSeconds =
|
||||
Number.isFinite(ttlRaw) && ttlRaw > 0 ? Math.floor(ttlRaw) : 604_800;
|
||||
|
||||
const accessToken = issueDocverToken(claims, secret, ttlSeconds);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
access_token: accessToken,
|
||||
token_type: "Bearer",
|
||||
expires_in: ttlSeconds,
|
||||
}),
|
||||
{ headers: { "content-type": "application/json; charset=utf-8" } },
|
||||
);
|
||||
}
|
||||
|
|
@ -5,11 +5,15 @@
|
|||
// 2) add-in ใน Word: ไม่มี x-api-key + body {ticket_data, HmacSha256} — server
|
||||
// ตรวจ HMAC ด้วย env ฝั่ง server (ADDIN_TICKET_SECRET → SECRET →
|
||||
// "super_secret", lib/addin-ticket.ts) แล้วออก token จาก claims ของ ticket
|
||||
// (secret ตัวเซ็น JWT ไม่มีทางไปอยู่ใน bundle ของ add-in) — ticket หมดอายุ
|
||||
// ยังออกให้ต่อเมื่อ fid นี้เคยมี version โดย uid เดียวกัน (requirement ขั้น 7)
|
||||
// (secret ตัวเซ็น JWT ไม่มีทางไปอยู่ใน bundle ของ add-in — client ไม่ตรวจ HMAC
|
||||
// เองแล้วตาม PLAN-SSO.md D13) · contract 2026-11-04: 400 = ฟอร์แม็ตพัง
|
||||
// (รวม missing) / 401 = ลายเซ็นไม่ถูก / 403 = ลายเซ็นถูกแต่ ticket หมดอายุ
|
||||
// (client ที่มี JWT เดิมของ fid นี้ใน session ใช้ต่อเอง — ไม่มีการออกซ้ำ
|
||||
// จากหลักฐาน DB อีกต่อไป เลิก requirement ขั้น 7 ฉบับ 2026-10-28)
|
||||
// ทั้งสองทางออก JWT HS256 เซ็นด้วย env DOCVER_TOKEN_SECRET อายุตาม env
|
||||
// DOCVER_TOKEN_TTL_SECONDS (default 3600) แล้วนำไปแนบ Authorization: Bearer กับ
|
||||
// endpoint อื่นของกลุ่ม (lib/docver-token.ts) · segment คงที่ "token" ไม่ชน [docId]
|
||||
// DOCVER_TOKEN_TTL_SECONDS (default 604800 = 7 วัน — token อายุยาวแทนระบบ
|
||||
// refresh/renew) แล้วนำไปแนบ Authorization: Bearer กับ endpoint อื่นของกลุ่ม
|
||||
// (lib/docver-token.ts) · segment คงที่ "token" ไม่ชน [docId]
|
||||
// (Next ให้ static ชนะ) และ middleware ยกเว้น api/documents/* อยู่แล้ว
|
||||
import { docverError } from "@/lib/document-versioning";
|
||||
import {
|
||||
|
|
@ -18,7 +22,6 @@ import {
|
|||
ticketToDocverClaims,
|
||||
verifyAddinTicketHmac,
|
||||
} from "@/lib/addin-ticket";
|
||||
import { hasVersionByUser } from "@/lib/docver-store";
|
||||
import { isTicketExpired } from "@/utils/docver-ticket";
|
||||
import {
|
||||
issueDocverToken,
|
||||
|
|
@ -30,8 +33,10 @@ import {
|
|||
export const runtime = "nodejs";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** 401 ของทางเข้า ticket — ข้อความเดียวทุกสาเหตุ (HMAC ผิด/รูปผิด/หมดอายุ/ไม่ส่งมา) กัน oracle */
|
||||
const TICKET_DENIED = "ticket ไม่ถูกต้องหรือหมดอายุ";
|
||||
/** 401 ของทางเข้า ticket — ข้อความเดียวทุกสาเหตุ (HMAC ผิด/รูปผิด) กัน oracle · หมดอายุแยกเป็น 403 */
|
||||
const TICKET_DENIED = "ticket ไม่ถูกต้อง";
|
||||
/** 403 ของทางเข้า ticket — ลายเซ็นถูกแต่หมดอายุ (client มี JWT เดิมใน session ก็ใช้ต่อได้) */
|
||||
const TICKET_EXPIRED = "ticket ของ e-Court หมดอายุ";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
return request.headers.get("x-api-key") !== null
|
||||
|
|
@ -65,25 +70,20 @@ async function postWithTicket(request: Request): Promise<Response> {
|
|||
}
|
||||
const parsed = parseTicketTokenBody(raw);
|
||||
if (!parsed.ok) {
|
||||
// missing = ไม่มี ticket มาเล่น ๆ (401 เดียวกับตอน verify ไม่ผ่าน)
|
||||
// invalid = รูป body ผิดจริง ๆ ให้ 400 ตาม convention กัน typo เงียบ ๆ
|
||||
return docverError(parsed.error, parsed.kind === "invalid" ? 400 : 401);
|
||||
// contract 2026-11-04: ฟอร์แม็ตพัง = 400 ทั้ง missing/invalid (เดิม missing เป็น 401)
|
||||
return docverError(parsed.error, 400);
|
||||
}
|
||||
// ตรวจ HMAC ก่อนเสมอ (ตัวตนของผู้ถือไฟล์) — exp ไม่ใช่เกณฑ์ตัดสิทธิ์ ณ จุดนี้
|
||||
const verified = verifyAddinTicketHmac(parsed.ticketData, parsed.hmacHex, addinTicketSecret());
|
||||
if (!verified.ok) return docverError(TICKET_DENIED, 401);
|
||||
|
||||
// ticket หมดอายุ → ยังออก token ให้ต่อเมื่อ "เอกสารนี้เคยทำงานโดย uid เดียวกัน"
|
||||
// (requirement ขั้น 7: กลับมาทำเอกสารเดิมผ่านได้แม้ ticket หมดอายุ — พิสูจน์จาก
|
||||
// document_versions ของกลุ่มนี้เอง) · DB พัง = fail closed เป็น 401 เหมือนไม่มีสิทธิ์
|
||||
// ลายเซ็นถูกแต่ ticket หมดอายุ → 403 เสมอ (contract 2026-11-04 — เลิก requirement
|
||||
// ขั้น 7 ฉบับ 2026-10-28: ไม่ตรวจ hasVersionByUser อีกต่อไป เพราะ ticket ฝังอยู่ใน
|
||||
// ไฟล์ ใครได้ไฟล์ก็ได้ ticket การปล่อยให้ ticket เก่าแลก token ใหม่ได้ตลอดไป
|
||||
// เท่ากันคือปล่อยสิทธิ์ตามการครอบครองไฟล์ — ฝั่ง client ที่มี JWT ของ fid นี้
|
||||
// ใน session ที่ยังไม่หมด (7 วัน) ใช้ต่อเอง ไม่มี = ขอไฟล์ใหม่จาก e-Court)
|
||||
if (isTicketExpired(verified.ticket.exp)) {
|
||||
let workedBefore = false;
|
||||
try {
|
||||
workedBefore = await hasVersionByUser(verified.ticket.fid, verified.ticket.uid);
|
||||
} catch {
|
||||
workedBefore = false;
|
||||
}
|
||||
if (!workedBefore) return docverError(TICKET_DENIED, 401);
|
||||
return docverError(TICKET_EXPIRED, 403);
|
||||
}
|
||||
return issueToken(ticketToDocverClaims(verified.ticket));
|
||||
}
|
||||
|
|
@ -96,8 +96,9 @@ async function issueToken(claims: DocverTokenClaims): Promise<Response> {
|
|||
return docverError("DOCVER_TOKEN_SECRET ยังไม่ถูกตั้งค่า", 500);
|
||||
}
|
||||
const ttlRaw = Number(process.env.DOCVER_TOKEN_TTL_SECONDS);
|
||||
// default 7 วัน (2026-11-04) — token อายุยาวแทนระบบ refresh/renew ของ add-in
|
||||
const ttlSeconds =
|
||||
Number.isFinite(ttlRaw) && ttlRaw > 0 ? Math.floor(ttlRaw) : 3600;
|
||||
Number.isFinite(ttlRaw) && ttlRaw > 0 ? Math.floor(ttlRaw) : 604_800;
|
||||
|
||||
const accessToken = issueDocverToken(claims, secret, ttlSeconds);
|
||||
return new Response(
|
||||
|
|
|
|||
|
|
@ -1,88 +1,129 @@
|
|||
// app/page.tsx (ระบบ B)
|
||||
'use client';
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const revalidate = 0;
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import Link from "next/link";
|
||||
import type { ChangeEvent } from 'react';
|
||||
import DependentDropdown, { DropdownData } from '@/components/utils/DependentDropdown';
|
||||
import TmplHome from "@/components/TmplHome";
|
||||
import { DropdownData } from '@/components/utils/DependentDropdown';
|
||||
import {
|
||||
Bookmark,ToyBrick,FilePenLine,BicepsFlexed,Rocket,Workflow, Files,Blocks,FileQuestionMark,
|
||||
ExternalLink,
|
||||
FileText,
|
||||
Highlighter,
|
||||
MessagesSquare,
|
||||
ToyBrick, FilePenLine, Rocket, Workflow, NotepadTextDashed,
|
||||
Blocks, FileQuestionMark, ShieldLock, FileUp, GitMerge,
|
||||
FileText
|
||||
} from "lucide-react";
|
||||
|
||||
|
||||
|
||||
|
||||
export default function HomePage() {
|
||||
const templateData: DropdownData = {
|
||||
T_X: ['T_no_warning', 'TF_no_warning_P2', 'TS_P2_IN'],
|
||||
T12_X: ['T12_b2_IN', 'T12_FD155_3s-long'],
|
||||
};
|
||||
const handleTmplSelect = (template: string, data: string) => {
|
||||
//console.log('Location Selected:', { template, data });
|
||||
};
|
||||
const handleInput = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
// console.log(e.target)
|
||||
return true;
|
||||
// อ่านไฟล์จากโฟลเดอร์ public/tmpl บน Server
|
||||
const tmplDirectory = path.join(process.cwd(), 'public/tmpl');
|
||||
let filenames: string[] = [];
|
||||
|
||||
try {
|
||||
filenames = fs.readdirSync(tmplDirectory);
|
||||
} catch (error) {
|
||||
console.error("ไม่พบโฟลเดอร์ public/tmpl", error);
|
||||
}
|
||||
|
||||
// กรองเฉพาะไฟล์ .docx และ .json
|
||||
const docxFiles = filenames.filter(file => file.endsWith('.docx'));
|
||||
const jsonFiles = filenames.filter(file => file.endsWith('.json'));
|
||||
|
||||
// สร้างโครงสร้างข้อมูลสำหรับ DependentDropdown แบบอัตโนมัติ (หรือปรับเงื่อนไขการจัดกลุ่มตามต้องการ)
|
||||
// ตัวอย่างนี้จัดกลุ่มตามชื่อไฟล์ตั้งต้น
|
||||
const templateData: DropdownData = {};
|
||||
docxFiles.forEach(docx => {
|
||||
const key = docx.replace('.docx', '');
|
||||
// หาไฟล์ json ที่ชื่อขึ้นต้นตรงกันมาใส่เป็นตัวเลือกย่อย
|
||||
const matchedJsons = jsonFiles
|
||||
.filter(json => json.startsWith(key))
|
||||
.map(json => json.replace('.json', ''));
|
||||
|
||||
templateData[key] = matchedJsons
|
||||
// templateData[key] = matchedJsons.length > 0 ? matchedJsons : ['default'];
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ padding: '2rem' }}>
|
||||
<div className="mx-auto max-w-4xl px-4 pb-16 pt-12 sm:px-6">
|
||||
<h1 className="font-display text-3xl tracking-tight text-ink sm:text-headline">
|
||||
eCourt Template
|
||||
DOCEDIT Template & Add-ins
|
||||
</h1>
|
||||
<p className="mt-2 text-body text-ink-soft">
|
||||
ระบบสร้างเทมเพลตเอกสารแบบใหม่ MS Word จากไฟล์ JSON และ Template docx
|
||||
ชุดพัฒนาระบบสร้างเอกสาร docx จากข้อมูลและเทมเพลต นำไปแก้ไขจัดหน้าต่อบน MS Word โดยที่ข้อมูลในฟอร์มเอกสารสามารถสะกัดออกมาใช้งานต่อได้ ควบคุมรุ่นของเอกสารด้วย Add-ins สามารถผสานกับระบบงานเดิมได้แบบไร้รอยต่อ
|
||||
</p>
|
||||
|
||||
{/* ส่วนเนื้อหาคงที่ (Features, Flow, Add-ins) */}
|
||||
<section className="mt-10">
|
||||
<div className="border-b border-line pb-3">
|
||||
<h2 className="font-display text-section-title text-ink">
|
||||
ระบบนี้ทำอะไรได้
|
||||
คุณสมบัติของระบบ
|
||||
</h2>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-5 sm:grid-cols-2">
|
||||
{[
|
||||
{
|
||||
icon: FileText,
|
||||
title: "DOCX Template",
|
||||
title: "ออกแบบเทมเพลตด้วย Microsoft Word",
|
||||
detail:
|
||||
"ออกแบบหน้าของเอกสารจาก Microsoft Word โดยไม่ต้องใช้เครื่องมือพิเศษ " +
|
||||
"สามารถนำเอกสารตัวเดิมมาใช้ออกแบบได้เลย " +
|
||||
"ไม่จำเป็นต้องมีความรู้ด้านการเขียนโปรแกรมหรือ XML ก็สามารถสร้างเทมเพลตได้",
|
||||
"สร้างเทมเพลตเอกสารได้ง่าย ๆ ด้วย Microsoft Word เครื่องมือที่ทุกคนคุ้นเคย " +
|
||||
"ไม่จำเป็นต้องติดตั้งโปรแกรมเพิ่มเติมหรือเรียนรู้เครื่องมือเฉพาะทาง สามารถนำเอกสาร Word ที่มีอยู่เดิมมาปรับใช้เป็นเทมเพลตได้ทันที " +
|
||||
"ไแม้ไม่มีทักษะด้านการเขียนโปรแกรม ก็สามารถออกแบบและสร้างเทมเพลตเอกสารได้ด้วยตนเอง ",
|
||||
},
|
||||
{
|
||||
icon: NotepadTextDashed,
|
||||
title: "รองรับเอกสารที่มีเงื่อนไขซับซ้อน",
|
||||
detail:
|
||||
"รองรับการสร้างเอกสารที่มีโครงสร้างและเงื่อนไขซับซ้อน ไม่ว่าจะเป็นข้อมูลจำนวนไม่แน่นอน การแสดงผลตามเงื่อนไข " +
|
||||
"หรือการจัดรูปแบบเนื้อหาแบบไดนามิก สามารถเพิ่มชุดคำสั่งและเขียนโปรแกรมลงในเทมเพลตได้ เช่น FOR, IF, " +
|
||||
"การแทรกโค้ด และฟังก์ชันเพิ่มเติม พร้อม API สำหรับนักพัฒนา เพื่อทดสอบการสร้างเอกสารจากเทมเพลต DOCX ร่วมกับชุดข้อมูล JSON ได้อย่างสะดวก "
|
||||
},
|
||||
{
|
||||
icon: FilePenLine,
|
||||
title: "Fill Form",
|
||||
title: "เปลี่ยนเอกสาร Word ให้เป็นแบบฟอร์มอัจฉริยะ",
|
||||
detail:
|
||||
"รอรับ Rich และ Plain Text Content Control " +
|
||||
"สามารถสร้างฟอร์มสำหรับกรอกข้อมูลพร้อมข้อมูลตั้งต้น " +
|
||||
"ผู้ใช้สามารถนำไปกรอกข้อมูลใน Word ได้เลย และสามารถเใช้โปรแกรมดึงข้อมูลกับมาเก็บในฐานข้อมูลได้",
|
||||
"รองรับ Rich Text และ Plain Text Content Controls ใน Microsoft Word " +
|
||||
"สามารถนำไฟล์ DOCX มาสร้างเป็นแบบฟอร์มสำหรับกรอกข้อมูลได้ทันที โดยไม่ต้องเสียเวลาออกแบบฟอร์มใหม่บน " +
|
||||
"Web Application พร้อม API สำหรับดึงข้อมูลที่ผู้ใช้กรอกกลับมาในรูปแบบ JSON เพื่อนำไปประมวลผล "+
|
||||
"จัดเก็บลงฐานข้อมูล หรือเชื่อมต่อกับกระบวนการทำงานอื่น ๆ ได้อย่างยืดหยุ่น ",
|
||||
},
|
||||
{
|
||||
icon: ToyBrick,
|
||||
title: "Integrate with Other System",
|
||||
title: "เชื่อมต่อระบบงานเดิมได้อย่างอิสร",
|
||||
detail:
|
||||
"มี Web API สำหรับสร้างเอกสารจากเทมเพลต(DOCX) กับข้อมูล(JSON) " +
|
||||
"มี API Key เพื่อจำกัดการใช้งาน สามารถออกบัตรผ่านที่ลงลายเซ็นต์ด้วย HmacSha256" +
|
||||
"เพื่อให้ผสานกับระบบงานอื่นๆ ได้อย่างปลอดภัย",
|
||||
"ออกแบบให้ทำงานเป็นระบบอิสระ สามารถเชื่อมต่อกับระบบงานที่มีอยู่เดิมผ่าน API ได้อย่างยืดหยุ่น รองรับการออก Access Token " +
|
||||
"สำหรับกำหนดสิทธิ์และควบคุมการเข้าถึง โดยไม่จำเป็นต้องพึ่งพาระบบ Single Sign-On (SSO) " +
|
||||
"หรือเชื่อมต่อเข้าถึงฐานข้อมูลของระบบงานเดิม ช่วยลดความซับซ้อนในการบูรณาการระบบและเพิ่มความคล่องตัวในการพัฒนา ",
|
||||
},
|
||||
{
|
||||
icon: FileText,
|
||||
title: "Dynamic Document",
|
||||
icon: GitMerge,
|
||||
title: "จัดการเวอร์ชันเอกสารอย่างเป็นระบบ",
|
||||
detail:
|
||||
"รองรับเทมเพลตที่ซับซ้อน ข้อมูลที่มีไม่แน่นอนอย่างอาเรย์ การแสดงผลตามเงื่อนไขแบบต่างๆ " +
|
||||
"สามารถเขียนโปรแกรมเพิ่มลงในเทมเพลตได้ รองรับ FOR , IF แทรกโค้ด " +
|
||||
"ฟังก์ชั่นในตัวเพื่อรองรับการสร้าง Content Control Bookmark ฯลฯ",
|
||||
"รองรับการจัดเก็บและบริหารจัดการเวอร์ชันของเอกสารร่วมกันผ่าน Microsoft Word Add-ins " +
|
||||
"สามารถบันทึกเวอร์ชันใหม่ เรียกดูประวัติการแก้ไข และเรียกคืนเวอร์ชันที่ต้องการกลับมาแก้ไขต่อได้ " +
|
||||
"ช่วยให้การทำงานร่วมกันเป็นระบบ ลดความสับสนจากการจัดเก็บไฟล์หลายฉบับ และสามารถตรวจสอบย้อนกลับไปยังเวอร์ชันก่อนหน้าได้",
|
||||
},
|
||||
{
|
||||
icon: Rocket,
|
||||
title: " High Performance",
|
||||
title: "ประสิทธิภาพสูง พร้อมรองรับการขยายระบบ",
|
||||
detail:
|
||||
"containerization ขยายระบบได้ง่าย, ระบบเทมเพลตใช้การ XML โดยตรง " +
|
||||
"การสร้าง template เป็นแบบ Multi-Thread รองรับ CPU หลายแกน " +
|
||||
"",
|
||||
"พัฒนาด้วยสถาปัตยกรรมแบบ Container รองรับการขยายระบบ (Scalability) ได้อย่างยืดหยุ่น " +
|
||||
"กระบวนการสร้างเอกสารจากเทมเพลตทำงานแบบ Multi-threading สามารถใช้ประโยชน์จาก CPU หลายแกนประมวลผลพร้อมกัน " +
|
||||
"รองรับการจัดเก็บไฟล์ผ่าน Object Storage ที่มีความยืดหยุ่นและรองรับการขยายพื้นที่จัดเก็บ ช่วยเพิ่มประสิทธิภาพและรองรับปริมาณงานที่เพิ่มขึ้นในอนาคต ",
|
||||
},
|
||||
{
|
||||
icon: FileUp,
|
||||
title: "ส่งออก PDF ด้วยรูปแบบที่ตรงกับต้นฉบับ",
|
||||
detail:
|
||||
"สร้างไฟล์ PDF โดยอัตโนมัติทุกครั้งที่บันทึกเวอร์ชันเอกสาร ผ่าน Microsoft Word โดยตรง ทำให้รูปแบบและการจัดวางหน้าของ PDF " +
|
||||
"สอดคล้องกับเอกสารต้นฉบับ ลดปัญหาความคลาดเคลื่อนของรูปแบบเอกสารจากความแตกต่างของ Microsoft Office แต่ละเวอร์ชัน " +
|
||||
"นอกจากนี้ยังประมวลผลบนเครื่องผู้ใช้งาน (Client-side Processing) ช่วยลดภาระการประมวลผลและการใช้ทรัพยากรของเซิร์ฟเวอร์",
|
||||
},
|
||||
{
|
||||
icon: ShieldLock,
|
||||
title: "ปลอดภัยด้วยมาตรฐานการสื่อสารและการควบคุมการเข้าถึง",
|
||||
detail:
|
||||
"ให้ความสำคัญกับความปลอดภัยของข้อมูล ด้วยการเข้ารหัสการสื่อสารผ่าน HTTPS และควบคุมการเข้าถึงไฟล์ด้วย Access Token " +
|
||||
"ที่ผ่านการลงลายมือชื่อดิจิทัลด้วย HMAC-SHA256 ช่วยป้องกันการปลอมแปลง Token และจำกัดการเข้าถึงทรัพยากรตามสิทธิ์ที่ได้รับ ลดความเสี่ยงจากการเข้าถึงข้อมูลโดยไม่ได้รับอนุญาต",
|
||||
|
||||
},
|
||||
].map(({ icon: Icon, title, detail }) => (
|
||||
<div
|
||||
|
|
@ -101,95 +142,41 @@ export default function HomePage() {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
{/* ส่วนตัวอย่างการใช้งานที่ต้องการให้อัปเดตอัตโนมัติ */}
|
||||
<section className="mt-10">
|
||||
<div className="border-b border-line pb-3">
|
||||
<h2 className="font-display text-section-title text-ink">
|
||||
ตัวอย่างการใช้งาน
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<TmplHome templateData={templateData} />
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
<section className="mt-10">
|
||||
<div className="mt-5 grid gap-5 sm:grid-cols-2">
|
||||
|
||||
<div className="flex gap-4 rounded-xl border border-line bg-white p-6">
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/[0.08] text-primary">
|
||||
<ExternalLink className="h-5 w-5" aria-hidden />
|
||||
<FileQuestionMark className="h-5 w-5" aria-hidden />
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="font-display text-subhead text-ink">Link (GET)</h3>
|
||||
<h3 className="font-display text-subhead text-ink">วิธีการใช้งาน</h3>
|
||||
<p className="mt-1.5 text-sm leading-relaxed text-ink-soft">
|
||||
|
||||
<a href="/docedit/api/template/download-docx?tid=T_X&fid=T_no_warning&uid=john&uname=John%20Doe">T_no_warning_john_xxx.docx</a><br />
|
||||
<a href="/docedit/api/template/download-docx?tid=T_X&fid=TF_no_warning_P2&uid=john&uname=John%20Doe">TF_no_warning_P2_john_xxx.docx</a><br />
|
||||
<a href="/docedit/api/template/download-docx?tid=T_X&fid=TS_P2_IN&uid=john&uname=John%20Doe">TS_P2_IN_john_xxx.docx</a><br />
|
||||
|
||||
<a href="/docedit/api/template/download-docx?tid=T12_X&fid=T12_b2_IN&uid=john&uname=John%20Doe">T12_b2_IN_john_xxx.docx</a><br />
|
||||
<a href="/docedit/api/template/download-docx?tid=T12_X&fid=T12_FD155_3s-long&uid=john&uname=John%20Doe">T12_FD155_3s-long_john_xxx.docx</a><br />
|
||||
|
||||
<a target="_blank" href="https://www.youtube.com/watch?v=QxMitSk3S4E" className="text-primary transition hover:underline">
|
||||
แนะนำเทมเพลตของ Docedit เบื้องต้น</a><br />
|
||||
<a target="_blank" href="https://www.youtube.com/watch?v=kOBoVN-KQzU" className="text-primary transition hover:underline">
|
||||
ติดตั้ง MS Word Add-ins</a><br />
|
||||
<a target="_blank" href="https://www.youtube.com/watch?v=P1-xe24nOUo" className="text-primary transition hover:underline">
|
||||
ใช้งาน DOCEDIT Add-ins</a><br />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 rounded-xl border border-line bg-white p-6">
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/[0.08] text-primary">
|
||||
<Files className="h-5 w-5" aria-hidden />
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="font-display text-subhead text-ink">Template & Data</h3>
|
||||
<p className="mt-1.5 text-sm leading-relaxed text-ink-soft">
|
||||
Template: <Link href="/tmpl/T_X.docx">T_X.docx</Link>,
|
||||
<Link href="/tmpl/T12_X.docx">T12_X.docx</Link><br />
|
||||
Data: <Link href="tmpl/TF_no_warning_P2.json">TF_no_warning_P2.json</Link>,
|
||||
<Link href="/tmpl/T_no_warning.json">T_no_warning.json</Link>,
|
||||
<Link href="/tmpl/TS_P2_IN.json">TS_P2_IN.json</Link><br />
|
||||
<Link href="/tmpl/T12_b2_IN.json">T12_b2_IN.json</Link>,
|
||||
<Link href="/tmpl/T12_FD155_3s-long.json">T12_FD155_3s-long.json</Link>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 rounded-xl border border-line bg-white p-6">
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/[0.08] text-primary">
|
||||
<FilePenLine className="h-5 w-5" aria-hidden />
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="font-display text-subhead text-ink">Form (POST)</h3>
|
||||
<div className="mt-1.5 text-sm leading-relaxed text-ink-soft">
|
||||
|
||||
<form action="/docedit/api/template/download-docx" method="POST">
|
||||
<DependentDropdown data={templateData} name1='tid' name2='fid'
|
||||
label1="Template(tid):" label2="ข้อมูล(fid):" onSelectChange={handleTmplSelect} />
|
||||
<div className="mb-2"></div>
|
||||
<div>
|
||||
<label htmlFor="uid">uid:
|
||||
<input type="text" name="uid" className="p-2 mb-2 border border-gray-300 rounded-md" defaultValue="john" />
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="uname">uname:
|
||||
<input type="text" name="uname" className="p-2 mb-2 border border-gray-300 rounded-md" defaultValue="John Doe" />
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
style={{
|
||||
padding: '10px 20px',
|
||||
backgroundColor: '#0070f3',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '5px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
ดาวน์โหลดเอกสาร MS Word
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 rounded-xl border border-line bg-white p-6">
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/[0.08] text-primary">
|
||||
<Workflow className="h-5 w-5" aria-hidden />
|
||||
|
|
@ -197,7 +184,7 @@ export default function HomePage() {
|
|||
<div>
|
||||
<h3 className="font-display text-subhead text-ink">Flow</h3>
|
||||
<div className="mt-1.5 text-sm leading-relaxed text-ink-soft">
|
||||
<div className="text-xl font-bold text-center">
|
||||
<div className="text-center">
|
||||
ลิงค์เลือกไฟล์<br />
|
||||
↓ <br />
|
||||
ตรวจสิทธิ์ สร้าง docx <br />
|
||||
|
|
@ -214,23 +201,11 @@ export default function HomePage() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 rounded-xl border border-line bg-white p-6">
|
||||
<span className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/[0.08] text-primary">
|
||||
<FileQuestionMark className="h-5 w-5" aria-hidden />
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="font-display text-subhead text-ink">วิธีการใช้งาน</h3>
|
||||
<p className="mt-1.5 text-sm leading-relaxed text-ink-soft">
|
||||
<Link href="https://www.youtube.com/watch?v=QxMitSk3S4E" className="focus-ring -m-1 inline-block rounded-md px-1 py-0.5 font-medium text-primary transition hover:underline">
|
||||
แนะนำเทมเพลตของ Docedit เบื้องต้น</Link><br />
|
||||
<Link href="https://www.youtube.com/watch?v=3emGNqRZgMs" className="focus-ring -m-1 inline-block rounded-md px-1 py-0.5 font-medium text-primary transition hover:underline">
|
||||
Sideload Office add-ins</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section className="mt-10">
|
||||
<div className="border-b border-line pb-3">
|
||||
<h2 className="font-display text-section-title text-ink">
|
||||
|
|
@ -245,24 +220,48 @@ export default function HomePage() {
|
|||
<h3 className="font-display text-subhead text-ink">
|
||||
Word Add-ins<br />
|
||||
</h3>
|
||||
<p className="mt-1.5 text-sm leading-relaxed text-ink-soft">
|
||||
ดาวน์โหลด
|
||||
<Link href="/wordaddin/manifest.xml" className="focus-ring -m-1 inline-block rounded-md px-1 py-0.5 font-medium text-primary transition hover:underline">
|
||||
manifest.xml </Link>
|
||||
ไว้ที่ SharePoint แชร์เน็ตเวิร์ก หรือดาวน์โหลดสคริปต์ PowerShell(Dev Install)
|
||||
<Link href="/wordaddin/install-word-addins.ps1" className="focus-ring -m-1 inline-block rounded-md px-1 py-0.5 font-medium text-primary transition hover:underline">
|
||||
install-word-addins.ps1 </Link>
|
||||
แล้วใช้คำสั่ง PowerShell ด้านล่างเพื่อติดตั้ง Add-ins ใน Word<br />
|
||||
<div className="mt-1.5 text-sm leading-relaxed text-ink-soft">
|
||||
การนำไปเผยแพร่สามารถติดตั้งได้หลายวิธี (แนะนำ Shared Folder Sideloading)
|
||||
<ol>
|
||||
<li>1. ติดตั้งผ่าน Office Store การเผยแพร่ต้องสมัครบัญชีนักพัฒนาใช้ Microsoft Partner Center ลงทะเบียนในโปรแกรม Office Store</li>
|
||||
<li>2. การติดตั้งระดับองค์กร (ผ่าน Microsoft 365 Admin Center) ทำตาม
|
||||
<a target="_blank" className="text-primary transition hover:underline" href="https://www.youtube.com/watch?v=KB_KRfAfJzY">
|
||||
วีดีโอนี้</a></li>
|
||||
<li>3. Sideloading ด้วยไฟล์ <Link target="_blank" href="/wordaddin/manifest.xml" className="text-primary transition hover:underline">
|
||||
manifest.xml </Link>(Office 365) Insert>Add-ins>My Add-ins คลิ้ก "Upload My Add-in"</li>
|
||||
<li>4. Shared Folder Sideloading(Office 2019+ รุ่นเดสท็อป) นำไฟล์ <Link target="_blank" href="/wordaddin/manifest.xml" className="text-primary transition hover:underline">
|
||||
manifest.xml </Link> ไปทำตามวีดีโอ <a target="_blank" href="https://www.youtube.com/watch?v=kOBoVN-KQzU" className="text-primary transition hover:underline">
|
||||
ติดตั้ง MS Word Add-ins</a> </li>
|
||||
<li>5. นักพัฒนา ใช้คำสั่ง reg.exe ร่วมกับ <Link target="_blank" href="/wordaddin/manifest.xml" className="text-primary transition hover:underline">
|
||||
manifest.xml </Link> ไปทำตามวีดีโอ <a target="_blank" href="https://www.youtube.com/watch?v=kOBoVN-KQzU" className="text-primary transition hover:underline">
|
||||
ติดตั้ง MS Word Add-ins</a> </li>
|
||||
<li>6. นักพัฒนาสร้างสคริปต์
|
||||
<Link href="/wordaddin/install-word-addins.ps1" className="text-primary transition hover:underline">
|
||||
install-word-addins.ps1 (เรียกใช้ reg.exe)</Link>
|
||||
สำหรับ Power Shell จะต้องให้สิทธิ์ในการเรียกใช้ สามารถเรียกคำสั่งด้านล่างเพื่อติดตั้ง<br />
|
||||
<br />
|
||||
<b className="whitespace-nowrap">Unblock-File .\install-word-addins.ps1</b><br />
|
||||
<b className="whitespace-nowrap">Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned -Force</b><br />
|
||||
<b className="whitespace-nowrap">.\install-word-addins.ps1 -Install</b><br /></li>
|
||||
</ol>
|
||||
<br />
|
||||
<b className="whitespace-nowrap">Unblock-File .\install-word-addins.ps1</b>
|
||||
<b className="whitespace-nowrap">Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned -Force</b>
|
||||
<b className="whitespace-nowrap">.\install-word-addins.ps1 -Install</b>
|
||||
|
||||
</p>
|
||||
<b>หมายเหตุ:</b>
|
||||
<i>
|
||||
Add-ins เป็นรุ่นใหม่(Office.js) มีความเป็นไปได้ที่จะรองรับรุ่น Desktop(Windows/Mac), Web และ iPad
|
||||
ด้วยข้อกำหนดของ TOR ให้รองรับ Word 2019(Desktop) บนวินโดว์ 10 เป็นขั้นต่ำ ทำให้ถูกจำกัดที่ WordApi 1.3 เท่านั้น
|
||||
ฟึเจอร์บางอย่างบน Add-ins จะถูกจำกัดตามความสามารถตามรุ่นของ WordApi การทดสอบต่างๆตามที่กำหนดใน TOR
|
||||
<br />
|
||||
สำหรับ Microsoft Office เวอร์ชันขายขาดแบบเก่า (เช่น Office 2016, 2019, 2021 หรือ 2024) จะไม่มีปุ่มอัปโหลดไฟล์ XML
|
||||
บนหน้าต่างเมนูโดยตรงเหมือน Microsoft 365 หากต้องการติดตั้งไฟล์ manifest.xml ในรุ่นดังกล่าว
|
||||
นะนำให้ใช้วิธีแชร์โฟลเดอร์เครือข่าย (Shared Folder Sideloading) หรือผ่าน Microsoft 365 Admin Center
|
||||
<br />
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue